You are on page 1of 98

www.edmaster.

it
Poste Italiane S.p.ASpedizione in A.P. D.L. 353/2003 (conv.in L.27/02/2004 n.46) art.1 comma 2 DCB ROMA Periodicit mensile APRILE 2007 ANNO XI N.4 (113)
PER ESPERTI E PRINCIPIANTI
RIVISTA+CD E6,90 RIVISTA+LIBRO+CD E9,90
VERSIONE PLUS VERSIONE STANDARD
Poste Italiane S.p.A Spedizione in A.P. D.L. 353/2003 (conv.in L.27/02/2004 n.46) art.1 comma 2 DCB ROMA Periodicit mensile NOVEMBRE 2008 ANNO XII, N.11 (132)
PER ESPERTI E PRINCIPIANTI
SOLUZIONI COME FUNZIONA E QUALI SONO GLI ALGORITMI IMPIEGATI
DALLE COMUNI APPLICAZIONI DI GRAFICA PER IMPLEMENTARE LA FUNZIONE
DI CLIPPING DI UNIMMAGINE
LIBRARY
GUIDA ALLUSO DELLA
LIBRERIA GNU SCIENTIFIC
Simulare il lancio di un dado
e studiarne la distribuzione
VB.NET
COME SI REALIZZA
UN EDITOR DI TESTO?
Sviluppare un software
con funzionalit simili a Word
RUBY
UN RISOLUTORE
DI SUDOKU
Implementare metodi euristici
per risolvere problemi complessi
FACEBOOK:
PROGRAMMALO COS
Creare un'applicazione utilizzando
la libreria Facebooker
JAVA
INTRODUZIONE AI
MESSAGE ORIENTED
MIDDLEWARE
Far parlare le nuove applicazioni
con un vecchio sistema
MOBILE
LA GRAFICA
DI BASSO LIVELLO
Controllo totale dei grafici
da plottare sul display
ECLIPSE
LA PRODUZIONE FINALE
DI UN SOFTWARE
Impacchettare unapplicazione
con tanto di eseguibili
e documentazione
DATABASE
COME FUNZIONA
NHIBERNATE
La libreria per la persistenza
di oggetti .NET su DB relazionali
JAVA
OPEN SOURCE:
JFREECHART
Guida alla generazione
dinamica di grafici
PYTHON
LE PRINCIPALI
NOVIT DI PY3K
Migrazione del codice legacy
e tutte le nuove funzionalit
PHP
ACCOUNT WEB:
COME GESTIRLI
Utilizzare i componenti del Zend
Framework e il paradigma MVC
20
SNIPPET
DI CODICE
da usare subito nei tuoi lavori
CHROME FAI DA TE
Ti insegnamo a realizzare un browser per navigare in Rete
con lo stesso motore impiegato dal software di Google
IL WIIMOTE TI OSSERVA!
Realizzare un rilevatore di movimento
che scatta la foto al momento giusto...
SISTEMA
Cosa si cela dietro il chiacchierato
sistema operativo mobile
TECNICA
Tutte le nozioni per progettare
software compatibile con Android
SOFTWARE
La nostra prima applicazione
per i nuovi cellulari HTC
PROVA IL SIMULATORE
per creare applicazioni Android
senza possedere il cellulare
sul CD-ROM
i
o
P
r
o
g
r
a
m
m
o






A
n
n
o

X
I
I

-

N

1
1

(
1
3
2
)

















































E
D
I
Z
I
O
N
I

M
A
S
T
E
R
S
V
I
L
U
P
P
A
R
E

P
E
R

A
N
D
R
O
I
D


B
R
O
W
S
E
R

F
A
I

D
A

T
E


U
N

E
D
I
T
O
R

D
I

T
E
S
T
O

S
U

M
I
S
U
R
A
SVILUPPARE APPLICAZIONI
PER IL NUOVO ANDROID
Anno XII - N.ro 11 (132) - Novembre 2008 -
Periodicit: Mensile
Reg. Trib. di CS al n.ro 593 del 11 Febbraio 1997
Cod. ISSN 1128-594X
E-mail: ioprogrammo@edmaster.it
http://www.edmaster.it/ioprogrammo
http://www.ioprogrammo.it
Direttore Editoriale: Massimo Sesti
Direttore Responsabile: Massimo Sesti
Responsabile Editoriale: Gianmarco Bruni
Vice Publisher: Paolo Soldan
Redazione: Raffaele Del Monaco
Collaboratori: E. Bottari, A. Bottoni, M. Buccio, F. Fortino,
F. Grimaldi, A. Leganza, G. Pignalberi, C. Pelliccia,
G.Pennella, F. Tomassetti, G. Valente
Segreteria di Redazione: Emanuela Giordano
Consulenza Redazionale: SET S.r.l.
Gianfranco Forlino
Realizzazione grafica: Cromatika S.r.l.
Art Director: Paolo Cristiano
Responsabile grafico di progetto: Salvatore Vuono
Responsabile area tecnica: Giancarlo Sicilia
Illustrazioni: M. Veltri
Impaginazione elettronica: Francesco Cospite, Lisa Orrico,
Nuccia Marra, Luigi Ferraro
Realizzazione Multimediale: SET S.r.l.
Realizzazione CD-Rom: Paolo Iacona
Pubblicit: Master Advertising s.r.l.
Via Amedei, 15 - 20123 Milano
Tel. 02 83121211 - Fax 02 83121207
e-mail advertising@edmaster.it
Sales Director ITC & Entertainment Magazines:
Max Scortegagna
Editore: Edizioni Master S.p.a.
Sede di Milano: Via Ariberto, 24 - 20123 Milano
Sede di Rende: C.da Lecco, zona ind. - 87036 Rende (CS)
Presidente e Amministratore Delegato: Massimo Sesti
Direttore Generale: Massimo Rizzo
ABBONAMENTO E ARRETRATI
ITALIA: Abbonamento Annuale: IOPROGRAMMO (11 NUMERI)
E59,90 SCONTO 21% SUL PREZZO DI COPERTINA DI E75,90
IOPROGRAMMO CON LIBRO (11 NUMERI) E75,90 SCONTO 30%
SUL PREZZO DI COPERTINA DI E108,90
Abbonamento Biennale: IOPROGRAMMO (22 NUMERI) E75,90
SCONTO 50% SUL PREZZO DI COPERTINA DI E151,80
IOPROGRAMMO CON LIBRO (22 NUMERI) E108,90 SCONTO 50%
SUL PREZZO DI COPERTINA DI E217,80 OFFERTE VALIDE FINO AL
30/11/2008.
Costo arretrati (a copia): il doppio del prezzo di copertina + E
5. 32 spese (spedizione con corriere). Prima di inviare i
pagamenti, verificare la disponibilit delle copie arretrate allo 02
833836.
La richiesta contenente i Vs. dati anagrafici e il nome della riv-
ista, dovr essere inviata via fax allo 02 83383610, oppure via
posta a EDIZIONI MASTER via C. Correnti, 1 - 20123 Milano, dopo
avere effettuato il pagamento, secondo le modalit di seguito
elencate:

cc/p n.16821878 o vaglia postale (inviando copia della ricevuta


del versamento insieme alla richiesta);

assegno bancario non trasferibile (da inviarsi in busta chiusa


insieme alla richiesta);

carta di credito, circuito Visa, Cartas, o Eurocard/Mastercard


(inviando la Vs. autorizzazione, il numero di carta di credito, la
data di scadenza, l'intestatario della carta e il codice CVV2, cio le
ultime 3 cifre del codice numerico riportato sul retro della carta).

bonifico bancario intestato a Edizioni Master S.p.A. c/o BCC


MEDIOCRATI S.C.AR.L. IBAN: IT 85 Q 07062 80881 000000012000
(inviando copia della distinta insieme alla richiesta).
SI PREGA DI UTILIZZARE IL MODULO RICHIESTA ABBONAMENTO PO -
STO NELLE PAGINE INTERNE DELLA RIVISTA. Labbonamento ver r
attivato sul primo numero utile, successivo alla data della ri chiesta.
Sostituzioni: qualora nei prodotti fossero rinvenuti difetti o
imperfezioni che ne limitassero la fruizione da parte dellutente,
prevista la sostituzione gratuita, previo invio del materiale
difettato. La sostituzione sar effettuata se il problema sar
riscontrato e segnalato entro e non oltre 10 giorni dalla data
effettiva di acquisto in edicola e nei punti vendita autorizzati,
facendo fede il timbro postale di restituzione del materiale.
Inviare il CD-Rom difettoso in busta chiusa a:
Edizioni Master - Servizio Clienti - Via C. Correnti, 1 - 20123
Milano
Assistenza tecnica: ioprogrammo@edmaster.it
Stampa: Arti Grafiche Boccia S.p.a. Via Tiberio Felice, 7 Salerno
Duplicazione CD-Rom: Neotek S.r.l.
C.da Imperatore - Bisignano (CS)
Distributore esclusivo per lItalia: Parrini & C S.p.A.
Via Santa Cornelia, 9 - 00060 - FORMELLO (RM)
Finito di stampare nel mese di Ottobre 2008
Nessuna parte della rivista pu essere in alcun modo riprodotta
senza auto riz zazione scritta della Edizioni Master. Manoscritti e
foto originali, anche se non pubblicati, non si restituiscono.
Edizioni Ma ster non sar in alcun caso responsabile per i danni
diretti e/o indiretti derivanti dallu ti lizzo dei pro gram mi contenuti
nel supporto mul ti me dia le allegato alla rivista e/o per even tua li
anomalie degli stessi. Nessuna responsabilit , inol tre, assunta
dalla Edizioni Master per danni o altro derivanti da virus
informatici non riconosciuti dagli antivirus ufficiali allatto della
ma ste riz zazione del supporto. Nomi e mar chi protetti sono citati
senza indicare i relativi brevetti.
Rispettare luomo e lambiente in cui esso vive e lavora una parte
di tutto ci che facciamo e di ogni decisione che prendiamo per
assicurare che le nostre operazioni siano basate sul continuo
miglioramento delle performance ambientali e sulla prevenzione
dellinquinamento
M
LUniverso Tecnologico
www.itportal.it
Servizio Abbonati:
tel. 02 833836
@ e-mail: servizioabbonati@edmaster.it
Allinizio di ogni articolo, troverete un simbolo che indicher la presenza di
codice e/o software allegato, che saranno presenti sia sul CD (nella posizione
di sempre \soft\codice\ e \soft\tools\)
sia sul Web, allindirizzo http://cdrom.ioprogrammo.it
J CD J WEB
nome_file.zip
Questo mese su ioProgrammo
PROGRAMMAZIONE 2.0
Certificato n.6168
del 11/12/2007
SISTEMA
Cosa si cela dietro il chiacchierato
sistema operativo mobile
TECNICA
Tutte le nozioni per progettare
software compatibili
con Android
SOFTWARE
La nostra prima applicazione
per i nuovi cellulari HTC

Certificato UNI EN ISO 14001


N.9191.EMAS
SVILUPPARE APPLICAZIONI
PER IL NUOVO ANDROID
Due punto zero. Queste sembrano ormai parole ma-
giche, il confine prima del quale Internet e tutta
linformatica si definiscono vecchie. C dellesage-
razione, certo, eppure vero che le pi interessanti
innovazioni tecnologiche sono nate proprio sotto il
segno del 2.0. La trasformazione dei siti in vere e
proprie applicazioni distribuite (vedi Gmail, Goo-
gle Documents, Netvibes) non solo modifica il na-
turale approccio degli utenti al Web ma deve com-
portare un rapido adeguamento degli sviluppatori ver-
so questa filosofia. ioProgrammo da sempre pi
interessata alle questioni tecnologiche rispetto a
quelle di business, ma innegabile che il fermento
attorno a Facebook e le migliaia di applicazioni per
questa piattaforma indicano una chiara direzione
anche per chi della programmazione fa un lavoro,
oltre che un piacere intellettuale. Quello che chi ama
la programmazione sa bene che importante te-
nere la mente aperta ed essere sempre disposti a
modificarsi per abbracciare le novit pi interes-
santi: ecco, per la programmazione 2.0 importan-
te acquisire il senso profondo del cambiamento. Il gran-
de aumento del flusso di dati tra client e server, una
conseguente maggiore attenzione alla sicurezza, lo
spostamento del baricentro computazionale dal
client ai server, la prevalenza del metadato sul da-
to: sono tutte questioni che prese singolarmente si
possono affrontare tatticamente ma che, andando
assieme, richiedono una vera rivoluzione nel modo
di approcciare lo sviluppo delle applicazioni. Come
sempre, sono novit che richiedono uno sforzo di
adattamento e, come sempre, dietro le difficolt si na-
scondono sempre delle occasioni. Con i progetti che
vi proporremo cercheremo di indagare queste op-
portunit, con la speranza di stimolare anche nuo-
ve idee e nuove applicazioni. Per restare nello spi-
rito 2.0, ci aspettiamo ovviamente che resti aperto
il dialogo con voi lettori e che da questa conversazione
scaturiscano le migliori idee. Scriveteci!
Due parole per salutare lottimo Piero Mannelli che
mi ha preceduto su ioProgrammo, e Gianfranco For-
lino, amico e maestro ritrovato: mi ha insegnato la te-
nacia, e abbiamo imparato assieme a trasformare
le idee in atti concreti.
Raffaele del Monaco
004-005 editoriale:004-005 editoriale 2-10-2008 15:49 Pagina 4
Questo mese su ioProgrammo
QUALCHE CONSIGLIO UTILE
I nostri articoli si sforzano di essere
com prensibili a tutti coloro che ci
seguono. Nel caso in cui abbiate
difficolt nel comprendere
esattamente il senso di una
spiegazione tecnica, utile aprire il
codice allegato allarticolo e seguire
passo passo quanto viene spiegato
tenendo docchio lintero progetto.
http://forum.ioprogrammo.it
casuali. La Gnu Scientific Library, a tal fine,
promette di fornire ottimi metodi.
Sfruttiamola per simulare il lancio di dadi e
vederne graficamente la distribuzione
SISTEMA
Movimenti sotto controllo col Wii
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 72
Un nuovo articolo per esaminare le
potenzialit del controller Wiimote. Vedremo
come adoperare lo stesso per realizzare, in
Flex e con lausilio di c#, un sistema per
scattare instantanee nel caso in cui si rilevi
un movimento
Python 3000 tutte le novit
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 76
La nuova versione rompe la compatibilit con
le vecchie e lo fa per una buona causa. Tra le
nuove caratteristiche, una maggiore
coerenza interna e maggiore eleganza del
codice
JMS: i messaggi asincroni
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 82
I sistemi basati su code di messaggi offrono
interessanti alternative rispetto ai tradizionali
metodi di interoperabilit in ambito
enterprise. A tal proposito scopriamo JMS con
Ti insegnamo a realizzare un browser per navigare in Rete
con lo stesso motore impiegato dal software di Google
pag. 12
Open Message Queue, il progetto open source
di Sun
Una libreria free per creare grafici
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 88
Un report, spesso, deve essere accompagnato
da grafici, che meglio aiutano a interpretare
quello che potrebbe sembrare un
incomprensibile groviglio di numeri. Ecco
allora che jfreechart pu essere una vera
manna dal cielo
Ruby on Rails sposa Facebook
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 94
In questo primo articolo dedicato alla
programmazione di Facebook, vedremo come
preparare l'ambiente di sviluppo, ci
interfacceremo con il sito di social networking
e inizieremo a interagire con i dati degli utenti
DATABASE
Introduzione a Nhibernate
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 98
Gli Object-Relational Mapping, sono sistemi
software in grado di effettuare
automaticamente la mappatura tra un DB e
le strutture tipiche di un linguaggio di
programmazione. Uno di questi, per .Net,
NHIbernate
SOLUZIONI
Il Clipping di una immagine
. . . . . . . . . . . . . . . . . . . . . . . . . pag. 110
Si tratta di una fase fondamentale nel
trattamento geometrico di unimmagine.
Consente di individuare le parti di oggetti che
effettivamente devono essere tracciate nella
finestra di schermo disponibile. Esaminiamone
il funzionamento
CHROME FAI DATE
Gli allegati di ioProgrammo
pag. 8
Il software in allegato alla rivista
Il libro di ioProgrammo
pag. 6
Il contenuto del libro in allegato
alla rivista
News pag. 10
Le pi importanti novit del
mondo della programmazione
Biblioteca pag. 30
I migliori testi scelti
dalla redazione
Tips & Tricks pag. 49
Una raccolta di trucchi da tenere
a portata di... mouse
Software pag. 106
I contenuti del CD allegato
a ioProgrammo
RUBRICHE
IOPROGRAMMO WEB
Zend Framework Tutorial
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 32
In questo nuovo appuntamento esamineremo
in dettaglio come estendere le nostre web
application per consentire alle stesse la
creazione e gestione di account utenti.
Alluopo faremo uso del paradigma MVC
SOFTWARE
La fase di building di un progetto
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 38
Questa ricetta spiega come esportare il
lavoro svolto in un progetto Java di Eclipse,
in modo da poter consegnare a terzi gli
eseguibili prodotti, la documentazione ed,
eventualmente, i sorgenti del proprio
software
SISTEMA
Un risolutore logico di Sudoku
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 42
Scopo di questo articolo implementare
unapplicazione che risolve il gioco del
Sudoku. Per fare questo adopereremo Ruby,
implementando metodi euristici e
avvantaggiandoci delle strutture proprie del
linguaggio
VB.NET 2008 e i Word Processor
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 52
In questarticolo vengono svelati alcuni
segreti che sono alla base dei word processor
di Microsoft. In particolare mostreremo come
implementare le classiche funzioni di un
editor, sfruttando il core del .Net framework
MOBILE
Videogiochi in Java per cellulari
(sesta parte)
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 60
Lo sviluppo di un gioco, oltre a prevedere
lutilizzo di componenti grafici ad alto
livello, spesso e volentieri necessita di
strutture grafiche a basso livello.
ci occuperemo proprio di queste ultime
LIBRARY
Studiamo il lancio dei dadi con le GNU
. . . . . . . . . . . . . . . . . . . . . . . . . . pag. 70
Abbiamo diversi modi per generare numeri
004-005 editoriale:004-005 editoriale 2-10-2008 15:50 Pagina 5
ht t p: / / www. i opr ogr ammo. i t
G
6
/Novembre 2008
Le versioni di ioProgrammo
RIVISTA
+
LIBRO
+
CD-ROM
in edicola
9
,90
h
V
e
r
s
io
n
e
P
L
U
S
I contenuti del libro
Q
uesto libro cerca di introdurre il
lettore in diverse tecnologie che
possono essere utilizzate con Java
per gestire un database. Saranno quindi
trattate tematiche come JDBC, per
instaurare una vera e propria connessione
con la base dati, ma anche famosi tool
come Hibernate, ORM che consente di
mappare un DB in una struttura Object
Oriented. Un intero capitolo dedicato a
iBates, progetto che si pone come
middleware tra la nostra applicazione
Object Oriented e il database. Un occhio di
riguardo anche alle ultime tecnologie,
come db4o, database Object Oriented che
dar allo sviluppatore nuovi spunti su
come concepire i database.
ARGOMENTI
Introduzione
JDBC: lAPI standard Java per collegarsi
ai database
Hibernate: famoso tool di ORM (Object Relational
Mapping)
iBatis il middleware tra la nostra applicazione
Object Oriented e il database
db4o: un database Object Oriented,
non relazionale, che offre agli sviluppatori
un nuovo modo di considerare il database
Altre tecnologie
JAVA & DATABASE
006 Presentaz Libro:008 1-10-2008 15:16 Pagina 6
ht t p: / / www. i opr ogr ammo. i t
G
8
/Novembre 2008
Posta elettronica con Visual basic
Le versioni di ioProgrammo
Come usare linterfaccia del CDRom
V
e
r
s
io
n
e
B
A
S
E
6
,90
h
RIVISTA
+
CD-ROM
in edicola
Festeggia dieci anni di attivit questo IDE
per programmare in Java, che fa della sem-
plicit d'utilizzo uno dei suoi punti di forza.
sviluppato direttamente in Java, per uti-
lizzarlo c' bisogno di avere installato alme-
no Java 1.6.
Molto personalizzabile, permette di salva-
re, compilare ed eseguire i programmi con
pochi clic del mouse.
Integra uno swing GUI designer per facili-
tare la creazione di interfacce grafiche e un
utilissimo correttore della sinstassi in realti-
me, che eviter errori proprio mentre si
digita il codice.
La funzione di autocompletamento permet-
te di accelerare il lavoro di stesura, cos
come l'autoindentazione mantiene ordina-
te e ben leggibili le migliaia di righe di codi-
ce sviluppate.
TJI JAVA
IDE 1.6
Il top software del mese
individuato dalla redazione
IN EVIDENZA
Il software diviso
in categorie per
una comoda consultazione
IL SOFTWARE
Torna alla pagina iniziale
del CD-ROM
HOME
Vuoi inviare una email alla
redazione con le tue richieste
CONTATTACI
Il database di tutti i software
pubblicati da ioProgrammo
anche gli arretrati
RICERCA SOFTWARE
Abbonamenti informazioni
e servizi utili
INFO
Lelenco del software
contenuto nelle categorie
IL SOFTWARE
La dimensione
del software sul CD
DIMENSIONE
Clicca qui per installare o
salvare il software sul tuo PC
SALVA
Una accurata recensione
dei contenuti
IL SOFTWARE
008 Presentaz CDRom 1:010 1-10-2008 15:22 Pagina 8
ht t p: / / www. i opr ogr ammo. i t
NEWS M
G
10
/Novembre 2008
News
GLI SVILUPPATORI
ITALIANI
DI VIDEGIOCHI
SI RIUNISCONO
I
l 22 novembre 2008, al DatchForum
di Assago, si terr la prima edizione
dellItalian Videogame Developers
Conference.
Organizzata dallAssociazione Italiana
Opere Multimediali Interattive in colla-
borazione con gli organizzatori della
prestigiosa Game Developers Confe-
rence di San Francisco, la conferenza
sar un interessante momento di in-
contro fra le varie figure professionali
coinvolte nello sviluppo di videogiochi,
dagli esperti grafici a developer di ogni
livello.
Unoccasione anche per chi aspira a en-
trare in un business che per fatturato
ha ormai, di fatto, superato quello del
cinema.
VOIP IN AZIENDA
CON LOPEN SOURCE
L
a societ Digum ha annunciato che,
grazie alla piattaforma VoIP Open
Source Asterisk, sta mettendo a pun-
to un modulo che, integrandosi con il
software Skype, consentir la gestio-
ne via centralino delle conversazioni
VoIP su base aziendale.
Si tratta di una notizia particolarmen-
te interessante e che mette in luce le
grandi potenzialit dellOpen Source e
delle sinergie con piattaforme softwa-
re proprietarie.
DA SUN
LA PIATTAFORMA
ENTERPRISE
PER WEB SERVICES
I
l 30 settembre scorso, a Santa Clara,
Sun Microsystems ha lanciato la Sun
OpenSSO Enterprise, una piattaforma
aperta per la gestione di Web Servi-
ces nel mercato professionale.
Sviluppata in collaborazione con OpenS-
SO (il pi grande progetto al mondo per
lo sviluppo di software Open Source),
rappresenta un soluzione altamente
scalabile e semplice da implementare.
Tra i pregi, la grande semplificazione
portata nel processo di gestione delle
identit fra applicativi diversi.
IL FUTURO DELLE
APPLICAZION WEB, A LONDRA
D
all8 al 10 ottobre si te-
nuto a Londra un sum-
mit con i protagonisti delle
applicazioni Web che stanno
cambiano il volto di Internet
ed il modo di lavorare di tut-
to il mondo. Mark Zuckerberg
di Flickr, Blain Cook di Twitter,
Kevin Rose di Digg e tanti al-
tri personaggi della pro-
grammazione 2.0 si sono ri-
trovati per approfondire me-
glio tutto quello che riguarda lattuale
evoluzione dellinformatica, con un in-
teressante punto di vista europeo.
Tra i temi della manifestazione cera infatti
Come costruire imprese di successo lon-
tano da Silicon Valley: indubbio che le
novit pi interessanti della program-
mazione dellinformatica in generale ven-
gano proprio dai siti cosiddetti sociali.
Lo stesso spostamento del baricentro
computazionale dal PC della scrivania ai
server cui ci si connette via Web ha por-
tato a una vera rivoluzione nellapproccio
allo sviluppo delle applicazioni. Al Futu-
re of Web Apps Expo si parlato proprio
delle ricadute di questa rivoluzione, sia
dal punto di vista tecnico che dal punto di
vista economico. Le nuove occasioni di
guadagno offerte dalle applicazioni per
Facebook e per tutti gli altri siti 2.0 sono
infatti ancora da esplorare e rappresentano
il pi promettente campo per i prossimi
anni.
http://london2008.futureofwebapps.com
IL NUOVO WINDOWS NELLE
MANI DEGLI SVILUPPATORI
I
n occasione della PDC 2008, Microsoft
distribuir in anteprima una release
pre-beta di Windows 7. Nel consueto
appuntamento con gli sviluppatori che,
anche questanno, avr luogo a Los
Angeles, il nuovo Sistema Operativo
certamente la novit pi attesa. Ai parte-
cipanti verr regalato un hard disk ester-
no con ben 160 GB di materiale relativo
alla conferenza, con tutte le piattaforme
e gli strumenti necessari allo sviluppo.
WEBKIT, CAMPIONE IN ACID3
I
l gruppo di sviluppo di WebKit
(motore di browser come Safari e
Chrome) ha annunciato di aver supe-
rato il test Acid3. Con questa afferma-
zione WebKit diventa il primo motore
al mondo a raggiungere questo tra-
guardo. I test Acid sono continuamen-
te aggiornati tenendo conto degli svi-
luppi del Web, e lultima versione del
test (rilasciata a marzo 2008) ha messo
in crisi tutti i maggiori browser presen-
ti sul mercato. La release 36882 di
WebKit ha ottenuto 100 su 100 nel test
Acid3, un risultato particolarmente
lusinghiero se paragonato agli 89 punti
di Firefox 3.1.
010-011:014-015 2-10-2008 11:24 Pagina 10
ht t p: / / www. i opr ogr ammo. i t
NEWS M
Novembre 2008/
11
G
L
a societ di comunicazione
HRP ha lanciato un nuovo si-
to in cui lutente pu scegliere
se navigare con il mouse oppu-
re attraverso dei semplici gesti
letti dalla webcam. Un inte-
ressante esperimento che anti-
cipa uno dei possibili esiti che
levoluzione dellinterazione uo-
mo/macchina sta affrontando.
Specialmente con il diffondersi di
dispositivi mobili in grado di na-
vigare, va aumentando lesigenza
di un tipo di interazione svinco-
lata dai consueti strumenti qua-
li mouse e simili. Il tempo e il
pubblico decreteranno il suc-
cesso della tecnologia pi con-
vincente. Se siete curiosi, il sito
questo: www.hrp.com
A
IL WEB? ORA SI NAVIGA A GESTI
I
l guru della Free Software Foundation si
scaglia contro quella che a molti sem-
bra essere una delle pi promettenti tec-
nologie in circolazione. Pare che lidea di
far ospitare i propri dati in centri di cal-
colo distanti dal proprio PC sia partico-
larmente invisa a Stallman, da sempre at-
tendo difensore della privacy degli uten-
ti. Riguardo alla possibilit di trasferire
limpegno computazionale dal PC a ser-
ver diffusi per il mondo, il giudizio al-
trettanto tranchant: non serve. Lattuale
potenza di calcolo dei PC pi che suffi-
ciente per qualsiasi applicazione e,
limponente flusso di dati di cui il cloud
computing necessita per Stallman solo
un enorme rischio per la sicurezza degli
utenti. Il pericolo pi grosso, dice in unin-
tervista rilasciata al Guradian, una sor-
ta di reazione a catena che si potrebbe
scatenare nel momento in cui a un uten-
te dovesse essere sottratto un account: si
metterebbe in pericolo tutta la sua atti-
vit lavorativa. Altro problema solleva-
to che con il cloud computing ci si af-
fida allonest di aziende che offrono
servizi in un primo momento poco co-
stosi (o magari gratuiti) ma che posso-
no poi diventare onerosi. Inoltre, anche
la cura di custodire i nostri dati personali
o aziendali viene cos demandata a sog-
getti terzi di cui difficile valutare lonest.
il vecchio problema del software open
contrapposto a quello proprietario: co-
noscere rigo per rigo cosa fa il codice
delle applicazioni che gestiscono i nostri
dati pi delicati sicuramente la ga-
ranzia maggiore: un investimento di fi-
ducia in entit pressoch sconosciute,
di fondo una scommessa.
D
al 26 al 28 settembre scorso ha avuto luogo a Pa-
lermo lannuale raduno degli hacker italiani. AllA-
sk 191, uno spazio autogestito, sono stati in circa set-
tecento a prendere parte a una serie di conferenze, proie-
zioni cinematografiche e dibattiti che avevano al cen-
tro il tentativo di andare verso una maggiore consape-
volezza sul ruolo dellinformatica e su quello degli hacker
nella societ. Numerose e interessanti anche le sessioni
tecniche, fra cui non mancata unapprofondita disa-
mina del Web 2.0 e della direzione che prender il Web
3.0. Lontano dai clich che vedono negli hacker una sor-
ta di terroristi informatici, lHackmeeting08 ha di-
mostrato che fra gli esperti di computer si cova una ge-
nerazione di pacifisti e ambientalisti che vogliono fare
delle loro capacit tecniche una leva per sollevare le sor-
ti dellumanit... magari partendo da una birra al bar!
STALLMAN: UN ANATEMA CONTRO IL CLOUD COMPUTING
HACKMEETING08 A PALERMO
010-011:014-015 2-10-2008 11:24 Pagina 11
ht t p: / / www. i opr ogr ammo. i t
COVER STORY M
G
12
/Novembre 2008
Browser fai da te! Come Chrome pi di Chrome
O
gni volta che il gigante dei motori di
ricerca rilascia un nuovo software, molti
gridano al miracolo, altri invece sono pi
critici, ma come si dice "bene o male, basta che
se ne parli"; anche questa volta Google ha creato
scompiglio nella rete rilasciando Chrome, brow-
ser minimale veloce e con le funzionalit pi uti-
lizzate dagli utenti. Un ritorno ad un'interfaccia
scarna e funzionale veramente cos innovativo
come alcuni credono? Lo vedremo in questo arti-
colo dove dimostreremo che con un po di inge-
gno e una buona conoscenza delle tecnologie
permettono a chiunque di realizzare un proprio
browser.
COSA SVILUPPEREMO
L'idea realizzata quella di un browser minima-
le che consenta ai genitori di limitare la naviga-
zione web dei propri figli, evitando che accedano
a siti non adatti alla loro et, e che non modifi-
chino impostazioni o installino program-
mi/virus/trojan senza alcun controllo. Avuta
l'intuizione, si prontamente cercato il modo
migliore per realizzare tale applicativo utilizzan-
do le tecnologie attualmente presenti:
l'attenzione caduta su AIR (Adobe Integrated
Runtime) framework complementare a Flex il cui
scopo quello di fornire accesso al sistema ope-
rativo e agli applicativi realizzati in Actionscript.
Oltre a queste infrastrutture software appena
citate utilizzeremo inoltre la DOM, il cui scopo
quello di permettere un accesso agevole (perch
strutturato) al contenuto della pagina visualizza-
ta.
I TEST ACID
Come capire se un browser capace di visualiz-
zare correttamente la maggior parte dei siti web,
se non tutti? Lo scopo dei test ACID (1, 2 e 3)
proprio quello di verificare quanto un browser
sia compilant agli standard definiti dal W3C.
Il World Wide Consortium, organismo costituito
dai colossi dell'informatica, responsabile dello
sviluppo degli standard utilizzati nel Web:
HTML, DHTML, XHTML, CSS, DOM, SVG, XML,
XSLT, PNG e tanti altri (per una lista completa
www.w3c.org). In realt molti browser sono anco-
ra indietro nell'essere perfettamente compilant
ed proprio per questa necessit che i tre test
ACID entrano in gioco: permettono di testarne le
effettive capacit (e limitazioni) semplicemente
visualizzando una pagina web. I test sono realiz-
zati in modo da verificare il rispetto di molte tec-
nologie, e i sottotest sono in un numero che
cresciuto esponenzialmente per andare incontro
al proliferare degli standard che sono stati appro-
vati negli ultimi anni. Particolarmente interes-
sante il test ACID 2 che corredato da un indi-
ce numerico da utilizzare come valore di "bench-
mark", per confrontare il livello di compatibilit
di browser diversi.
WEBKIT: IL CUORE
DI SAFARI E CHROME
La scelta caduta su FLEX/AIR specialmente per
la possibilit di utilizzare un componente in
grado di gestire semplicemente la navigazione,
mx:html, il cui motore di rendering il "famoso"
Webkit, lo stesso utilizzato dall'apprezzato Safari
e da Chrome. Questo browser un progetto open
Conoscenze richieste
OOP - Actionscrip 3 -
MXML
Software
Flex Builder oppure
FlashDevelop, Flex
SDK
Impegno
Tempo di realizzazione
REQUISITI
CREIAMO IL NOSTRO
GOOGLE CHROME
REALIZZIAMO VELOCEMENTE UN BROWSER WEB UTILIZZANDO IL MOTORE DI RENDERING
DI SAFARI E GOOGLE CHROME. VEDREMO CHE PROGETTARE E REALIZZARE UN BROWSER, ALLA
FIN FINE, NON POI UNOPERAZIONE COS COMPLICATA COME SOVENTE CI VIENE DA PENSARE
J CD J WEB
EasyWebSurfer.zip
cdrom.ioprogrammo.it
Fig. 1: L'interfaccia del nostro browser simil Chrome
012-018:032-035 30-09-2008 16:07 Pagina 12
source che ha lobiettivo di realizzare un suppor-
to completo a tutti gli standard web, e il cui svi-
luppo mirato al raggiungimento di alcuni
obiettivi ben precisi: essere sicuro, usabile e por-
tabile. L'engine ha ottenuto, nell'ultima build
rilasciata, un 100/100 sull'Acid3 (risultato rag-
giunto anche da Opera). Nei nostri test, Firefox
3.0.1 ha superato il 70/100, Internet Explorer
7.0.6001 invece ha di poco superato il 10/100,
confermando la necessit dell'uscita della ver-
sione 8, che, a detta Microsoft, riuscito ad esse-
re full compilant nell'Acid2; Google Chrome si
piazzato in testa con circa un 80/100. Purtroppo
la versione presente in Air alquanto obsoleta
poich restituisce un 40/100 nell'Acid3 e rivela
alcuni problemi nell'Acid2, ma resta comunque
pi performante di alcuni concorrenti (la dll
risulta essere compilata nel mese di maggio
2008); aggiornamenti futuri di Air, in cui si spera
verr modificata tale libreria, lo avvicineranno
sicuramente al 100/100. Da quanto comunicato
nella documentazione ufficiale di Air questa ver-
sione non supporta il metodo javascript print()
per la window, l'utilizzo di plugin (ad eccezione
del Flash Player e di Acrobat/Acrobat Reader),
l'SVG e la propriet opacity dei CSS; sono state
altres aggiunte funzionalit che permettono una
maggiore integrazione tra Actionscript e il conte-
nuto del browser.
QUESTIONE
DI VERSIONE...
Per ottenere i massimi benefici dalle migliorie
apportare ad AIR, che ricordiamo essere ancora in
fase beta, quindi non definitiva, necessario scari-
care l'ultimo SDK (3.2.0.3199) disponibile nel sito
opensource.adobe.com (Downloads->All Flex 3
Downloads) nella sezione Nightly Builds; questo
articolo stato aggiornato utilizzando proprio
l'ultima release di FLEX (contenente AIR 1.5, nome
in codice: Cosmo) perch in tale versione sono state
apportate migliorie nei motori (html rendering,
javascript con il nuovo motore SquirrelFish, text
rendering) e risolti numerosi bug. Il consiglio
quindi di aggiornare progressivamente la vostra
libreria anche settimanalmente, poich le modifi-
che e i miglioramenti sono continui; questa proce-
dura dovrebbe essere attuata anche dopo il rilascio
della versione definitiva di AIR perch lo sviluppo
di FLEX in continua evoluzione (gi sono disponi-
bili build della futura release 4!).
L'INTERFACCIA GRAFICA
Per realizzare questo software abbiamo creato un
nuovo progetto AIR e provveduto a creare due file
MXML: EasyWebNavigator e Browser; si utilizza-
to un TabNavigator, che permette di contenere un
numero indeterminato di tab, ognuno dei quali
sar un'instanza di un componente chiamato
Browser. Grazie alla possibilit offerta al pro-
grammatore da FLEX, che consiste nello sceglie-
re se realizzare dei nuovi componenti tramite
classi actionscript, oppure adoperando l'editor
grafico, abbiamo scelto questa seconda opzione
(creando il file Browser.mxml) per velocizzare il
processo e con lo scopo di ottenere un feedback
visivo immediato del risultato senza dover avvia-
re ripetutamente l'applicativo: andremo quindi a
creare tramite l'editor wysiwyg un componente
personalizzato contenente tutti i pulsanti e i
comandi necessari.
Il parent container utilizzato in questo caso un
VBox (componente che posiziona verticalmente
tutti gli elementi in sequenza) e nel quale andre-
mo a inserire un mx:HTML e una barra di naviga-
zione con i tipici pulsanti di controllo. Per utiliz-
zare tale componente all'interno del nostro file
principale (EasyWebSurfer.mxml) necessario uti-
lizzare nelle impostazioni della mx:WindowedAp -
plication il seguente comando:
xmlns:custom="*"
il cui scopo quello di rendere disponibili tutti i
componenti creati dall'utente semplicemente
invocando il costruttore (che deve essere senza
argomenti quando realizziamo tramite file mxml
questi oggetti grafici):
var brw:Browser= new Browser();
//Aggiungiamo nel tabNavigator un nuovo
componente
//posizionandolo subito dopo quello attuale
tabNavigator.addChildAt(brw,tabNavigator.selectedIn
ht t p: / / www. i opr ogr ammo. i t
M
COVER STORY
Novembre 2008/
13
G
Browser fai da te! Come Chrome pi di Chrome
NOTA
RISORSE WEB
Download dell'ultima
versione Flex disponibile
(sia closed che open
source):
http://opensource.adobe.
com/wiki/display/flexsdk/
Download+Flex+3
Documentazione e API AIR
e FLEX:
http://www.adobe.com/s
upport/documentation/e
n/air/
FlashDevelop, editor open
source compatbile con
AIR:
http://www.flashdevelop.
org/community/
Fig. 2: Una semplificazione della struttura data al brow-
ser
012-018:032-035 30-09-2008 16:07 Pagina 13
ht t p: / / www. i opr ogr ammo. i t
COVER STORY
M
G
14
/Novembre 2008
Browser fai da te! Come Chrome pi di Chrome
dex+1);
Il metodo utilizzato per creare un nuovo compo-
nente addBrowserTab(url:String) che contiene il
codice necessario per aggiungere una nuova
istanza del componente da noi creato.
LA DOCUMENT
OBJECT MODEL
La DOM (Document Object Model)
un'interfaccia, definita dal W3C (www.w3c.org/ -
DOM) e giunta alla versione finale (la 3) nel 2004,
che consente a programmi e script (ECMAScript
e tutte le sue implementazioni: Javascript/
JScript Actionscript e altre) di accedere e modifi-
care il contenuto di un documento HTML o
XML, precedentemente e contestualmente alla
sua visualizzazione; come scritto, lo utilizzere-
mo per analizzare e modificare, quando neces-
sario, la struttura stessa della pagina per adattar-
la alle nostre necessit. Abbiamo accesso a prati-
camente tutti gli attributi dei tag HTML: se
volessimo, ad esempio, modifcare un'immagine
di cui conosciamo solo l'ID? Baster richiamare
il seguente codice:
document.getElementById("myimgId").nomeattributo
= "value";
Potremo modificare il testo visualizzato quando
passiamo sopra di essa (il TITLE pu essere
impostato su qualunque elemento, mentre l'ALT
solo alle immagini e rappresenta il testo alterna-
tivo che viene visualizzato in caso non si
voglia/possa visualizzare tale risorsa):
document.getElementById("myimgId").title="My
comment on Title";
document.getElementById("myimgId").alt="My
comment on Alt";
Il codice che segue recupera tutti i link presenti
nella pagina, accedendo al terzo presente e
modificandone il comportamento (utile magari
per aprire una finestra aggiuntiva e collegarsi al
sito di Google):
var elenco = document.links;
elenco[2].target="_blank";
elenco[2].href="http://www.google.it";
Grazie al DOM possiamo inoltre creare, o modi-
ficare, lo style (CSS) applicato su qualunque ele-
mento:
document.getElementById("id").style.property="valu
e";
Non necessario assegnare un ID univoco ad
ogni elemento di una pagina web, anche se si
rivela il metodo pi semplice per accedervi.
possibile comunque "attraversare" la struttura
della pagina dinamicamente poich viene rap-
presentata con un albero: sono disponibili attri-
buti quali firstChild, lastChild, childnodes, parent
(che permette di risalire nella struttura). Di sicu-
ro aiuto nell'identificazione e nella comprensio-
ne della struttura di una pagina risulta la plugin
Firebug per Firefox; con Internet Explorer si pu
utilizzare l'Internet Explorer Developer Toolbar,
prelevabile gratuitamente dal sito Microsoft. In
AIR si accede al DOM utilizzato il campo
htmlLoader.window.document del componente
MX:HTML.
SEMPLICI
FUNZIONALIT
DI NAVIGAZIONE
Il componente mx:HTML gestisce automatica-
mente le operazioni basilari di navigazione, e
una history in maniera automatica: baster sem-
plicemente invocare i metodi historyBack(),
historyForward(), reload(), cancelLoad() per effet-
tuare le operazioni descritte dal relativo metodo.
Per spostarsi in una nuova pagina si dovr utiliz-
zare
oggettoMXHTML.location=url;
Tale assegnazione avvier immediatamente il
caricamento della pagina richiesta. Ho aggiunto
la possibilit di modificare la homepage attuale
cliccando con il destro del mouse sulla relativa
icona. I favoriti sono invece gestiti graficamente
con un combobox il cui dataProvider (la sorgen-
te dati) una arraycollection in cui vengono
aggiunti i vari indirizzi tramite il metodo
Fig. 3: Un esempio di come viene rappresentata
una pagina dal DOM
012-018:032-035 30-09-2008 16:07 Pagina 14
ht t p: / / www. i opr ogr ammo. i t
addItem(...); tale metodo (grazie alla struttura
dati che utilizziamo) ci consente di inserire per
ogni elemento una coppia di valori, costituita da
un campo in cui inserire l'oggetto desiderato
(value) e da una etichetta (label): il primo verr
utilizzato per ottenere, tramite il metodo
selectedItem.value fornito dal componente
mx:Combobox, il contenuto reale, mentre il
secondo sar visualizzato nel combobox come
voce selezionabile.
favouritesArraycollection = new ArrayCollection();
favouritesArraycollection.addItem({value:null,label:'F
avourites'});
favouritesArraycollection.addItem({value:'http:/
/www.google.it',label:'Search Engine'});
favouritesArraycollection.addItem({value:'http:/
/www.leganza.it',label:'Andrea Leganza'});
<MX:ComboBox id="favsComboBox"
dataProvider="{favouritesArraycollection}"
change="{checkSite(favsComboBox.selectedItem.val
ue)?htmlContainer.location=favsComboBox.selectedIt
em.value:null;}" selectedIndex="0" width="150">
</mx:ComboBox>
BARRA
DI AVANZAMENTO
Un utile riferimento visivo quello della barra di
avanzamento, il cui scopo quello di indicare il
protrarsi di un'operazione; in questa situazione,
non volendo (e non potendo) avere una stima
precisa, utilizzeremo la propriet indeterminate
del componente mx:ProgressBar, che verr modi-
ficata solo quando la location del componente
mx:html verr impostata; tale operazione far
scattare prima l'evento locationChange, poi com-
plete al termine del caricamenteo della pagina:
<mx:ProgressBar width="100%"
indeterminate="false" height="6" id="prgsBar"
label=""/>
<mx:HTML id="htmlContainer" ...
locationChange="...;prgsBar.indeterminate=true;}"
complete="{prgsBar.indeterminate=false;}" />
Lo stato indeterminato in realt attiva
un'animazione infinita che andremo a bloccare
quando non sar pi necessario. Andando pi in
dettaglio, sarebbe possibile analizzare la dimen-
sione della pagina e monitorare il contenuto
caricato per fornire una valutazione pi precisa
dei tempi necessari per il completamento dell'o-
perazione, semplicemente effettuando una
richiesta HTTP e analizzando la sequenza e i
contenuti dei messaggi scambiati.
MEMORIZZIAMO GLI
INDIRIZZI VISITATI
AIR permette di creare, accedere e modificare
qualunque file presente sul proprio computer.
Sfruttiamo proprio questa sua caratteristica per
tenere traccia degli spostamenti nel web effet-
tuati:
private functionsaveHistoryStep(param:String):void{
var file:File = File.applicationStorageDirectory.
resolvePath("history.txt");
var stream:FileStream = new FileStream();
//Aggiungiamo il nuovo link visitato, in caso il file
non esistesse verr creato.
stream.open(file, FileMode.APPEND);
stream.writeUTFBytes(param+"@"+new
Date().toLocaleString()+(File.lineEnding));
stream.close(); }
File.applicationStorageDirectory permette di uti-
lizzare un cartella, preimpostata da AIR, come
posizione dove salvare impostazioni e file di log;
avremmo potuto anche utilizzare desktopDi -
rectory o documentsDirectory o userDirectory.
abbiamo inoltre aggiunto la data e l'ora (penul-
tima riga di codice) per rendere pi comoda la
consultazione; la necessit di utilizzare
File.lineEnding obbligata per rendere tale appli-
cazione multipiattaforma (alcuni sistemi usano
carriage return seguito da line-feed, altri solo
line-feed). Proteggendo questo file tramite algo-
ritmi di cifratura (MD5 o SHA1 sono pi che suf-
ficienti) si evitarebbe qualunque possibile
manomissione (sempre che l'utente venga a
sapere della sua esistenza, evento poco probabi-
le utilizzando un nome, e magari anche
un'estensione, non esplicite).
CONTROLLIAMO
L'ACCESSO AI SITI WEB
I siti ammessi sono salvati in un file di testo (che
sarebbe opportuno criptare con MD5 o SHA1
come precedentemente accennato) caricato
all'avvio dell'istanza del browser:
private function readAllowedSitesFile():void{
var file:File = File.applicationStorageDirectory.
resolvePath("allowedsites.txt");
if (!file.exists) {
//Viene creato un file contenente solo
www.google.com e www.google.it
M
COVER STORY
Novembre 2008/
15
G
Browser fai da te! Come Chrome pi di Chrome
012-018:032-035 30-09-2008 16:07 Pagina 15
ht t p: / / www. i opr ogr ammo. i t
//in caso non fosse presente.
var stream:FileStream = new FileStream()
stream.open(file, FileMode.WRITE);
stream.writeUTFBytes("http://www.google.com@http
://www.google.it");
stream.close();
}
if (file.exists) {
var stream:FileStream = new FileStream()
stream.open(file, FileMode.READ);
//Popoliamo l'array con i siti ammessi
siteList = stream.readUTFBytes(
stream.bytesAvailable).split("@");
stream.close();
}}
siteList non altro che un array generato splittan-
do il contenuto del file allowedsites.txt, dove i vari
siti sono separati dalla "chiocciola". Appena il
DOM disponibile, analizziamo tutti i link per
rimuovere quelli appartenenti a quei domini non
consentiti, utilizzando il metodo manageLinks;
adoperiamo il metodo checksite per fare tale veri-
fica (riutilizzeremo tale funzione nel paragrafo
successivo, ecco il motivo perch non ho inserito
direttamente il suo codice all'iterno di
manageLinks).
private function manageLinks():void {
if (htmlContainer!=null) {
//Tramite il DOM accediamo all'elenco di
tutti i links della pagina
var links:Object =
htmlContainer.htmlLoader.window.document.links;
if (links!=null) {
for(var i:Number = 0; i <
links.length; i++) {
if (links[i].srcElement!="") {
if (links[i].tagName=="A") {
if (!checkSite(links[i].href)) {
links[i].href="#";
}}
}}}}
else callLater(manageLinks);
}
Il controllo viene effettuato ad ogni clic su un
link o inserimento nella barra degli indirizzi, e
avviene tramite una semplice ricerca: se le due
stringhe combaciano o se l'indirizzo corrente
contiene uno di quelli corretti, oppure se un
link interno, consentiamo la navigazione (ho evi-
tato anche qui le espressioni regolari ma solo per
chiarezza nel codice). Sostituiamo l'indirizzo
puntato dal link con il carattere #, utilizzato per
riferirsi alle ancore presenti in una pagina.
Notate la necessit di utilizzare il metodo
callLater, il cui scopo invocare, nel fotogramma
successivo della timeline, il metodo passato
come parametro se una (o pi) condizioni non
sono rispettate: questo ci permette di non incor-
rere nei tipici problemi di accesso a variabili (e
relativi metodi) non ancora inizializzate.
private function checkSite(value:String):Boolean {
changeFontSize = false
var URLfound:Boolean = false;
if ((value!=null)&&(value.length>0)) {
//Verifichiamo se esterno
if (value.search("http://")!=-1) {
//Iteriamo per tutta la lista dei link
for (var i:int=0;i<siteList.length;i++) {
if (!URLfound) {
//Verifichiamo che il link accettato non sia
vuoto
if ((siteList[i]!=null)&&((String)
(siteList[i]).length>0)) {
//Verifichiamo se i link
combaciano o siamo in un sotto indirizzo
if ((value==siteList[i])||
(value.search(siteList[i])!=-1)) {
//Valori restituiti da search,
diversi da -1 indicano che stata trovata
//una sottostringa.
URLfound = true
}
}
}}}
else {
//E' un link interno al sito
URLfound = true;
}
return URLfound;}
//Se l'url una stringa vuota o nulla
else return false;}
GESTIRE I LINK _BLANK
Il comportamento del componente mx:html,
quando si seleziona un link (tag HTML <A>) con-
tenente come attributo target=_blank (quelli che
dovrebbero richiamare una nuova finestra del
browser), poich non genera finestre figlie in
modo automatico, quello di reindirizzare se
stessa verso tale URL: questo comportamento
non molto funzionale e provvediamo ad aggira-
COVER STORY
M
G
16
/Novembre 2008
Browser fai da te! Come Chrome pi di Chrome
012-018:032-035 30-09-2008 16:07 Pagina 16
ht t p: / / www. i opr ogr ammo. i t
re il problema generando una nuova istanza del
browser quando selezioniamo tale tipologia di
indirizzo. Accediamo ai link della pagina, tramite
DOM, e modifichiamo il comportamento solo di
quelli in cui presente tale target:
var links:Object =
htmlContainer.htmlLoader.window.document.links;
if (links!=null) {
for(var i:Number = 0; i < links.length; i++) {
if ((links[i].target!="")&&(links[i].target=="_blank"))
{
//Associamo all'evento onomouseup l'apertura di un
nuovo tab se TARGET=_blank
links[i].onmouseup = function(obj:Object):void {
if (obj.srcElement!="") {
if (obj.srcElement.tagName=="A") {
Application.application.addBrowserTab(obj.srcElement
.href);
}
else if (obj.srcElement.tagName=="IMG") {
//Aggiungiamo una nuova istanza del nostro custom
component
Application.application.addBrowserTab(obj.srcElement
.parentNode.href);
}}}}
In questo caso si supportato sia il clic su link
testuale che su un'immagine: il comportamento
diverso dovuto al fatto che nel secondo caso
dobbiamo risalire al padre del tag, che risulta
essere il link vero e proprio (l'evento infatti non
risulta essere lanciato dal tag A, bens dal tag
interno ad esso: IMG). Una soluzione alternativa
quella di aprire una finestra del browser di
default del proprio computer abilitando la pro-
priet navigateInSystemBrowser disponibile trami-
te l'oggetto HTMLLoader. L'oggetto Application
servito per risalire nell'albero dei componeti flex
e invocare un metodo presente nel contenitore
principale di tutti i componenti (il file
EasyWebsurfer.mxml).
CERCARE UNA FRASE
DIRETTAMENTE DALLA
BARRA DELL'INDIRIZZO
Analizzando come viene generata da un motore
di ricerca la stringa utilizzata per descrivere le
parole cercate, passata tramite il metodo di invio
GET alla pagina che ne mostra i risultati, possi-
bile cercare un testo o una frase. Nel caso di
Google gli spazi sono sostituiti con dei +, mentre
la pagina di ricerca si chiama search?q=: a tale
stringa segue la lista di contenuti da cercare.
var text:String = "http://www.google.com/
search?q="+text.replace(" ","+");
htmlContainer.location= text;
Dove text proprio la parola/frase da voi cercata:
provvediamo quindi a generare tale indirizzo per
poi indirizzare la nostra istanza del browser
verso tale risorsa web. Ogni motore di ricerca ha
le sue regole, vi baster analizzare l'indirizzo
della pagina dei risultati per trovare le regole che
applica (una non malevola operazione di rever-
se engineering).
NASCONDERE
LE IMMAGINI
Uno dei pi comuni inconvenienti che si pu
verificare durante la navigazione la visualizza-
zione di immagini non adatte, in genere pubbli-
cit di altri siti: per nascondere tali immagini
baster iterare per l'elenco di tali contenuti
(sempre utilizzando il DOM) e filtrare quelli non
provenienti dallo stesso dominio in cui ci trovia-
mo: cerchiamo quelle immagini il cui indirizzo
dell'immagine contiene http:// oppure https://
(non scomodiamo le espressioni regolari per
questi semplici controlli) e nascondiamole utiliz-
zando la propriet visibility disponibile tramite
CSS; non abbiamo utilizzato display:none perch
in tal modo si perderebbe la formattazione dei
contenuti:
//Otteniamo tutte le immagini disponibili
var images:Object =
htmlContainer.htmlLoader.window.document.images;
//Iteriamo
if (images!=null) {
for(var j:Number = 0; j < images.length; j++) {
//Verifichiamo che non
if ((((String)(images[j].src).search("http://")!=-
1)||((String)(images[j].src).search("https://")!=-1)))
{
//Non un'immagine di questo dominio:
nascondiamola
images[j].style.visibility="hidden";
}}}
VARIARE LA
DIMENSIONE DEI TESTI
Si parla sempre pi di siti accessibili e in questo
caso abbiamo voluto fornire la possibilit di
variare le dimensioni dei caratteri delle pagine
che si visitano; questo possibile accedendo tra-
mite DOM al BODY della pagina html e aggiun-
M
COVER STORY
Novembre 2008/
17
G
Browser fai da te! Come Chrome pi di Chrome
012-018:032-035 30-09-2008 16:07 Pagina 17
ht t p: / / www. i opr ogr ammo. i t
gendo uno stile globale fontSize utilizzando come
unit la ems (altri utilizzerebbero la %, ma nessun
programmatore accorto i pixel):
...
//Preleviamo il nodo del DOM con il body
body = htmlContainer.htmlLoader.window.
document.getElementsByTagName("body")[0]
....
private function increaseFontSize():void {
changeFontSize = true;
fontValue+=DEFAULTMINCREASE;
if (body!=null) body.style.fontSize =
fontValue+"em";
else callLater(increaseFontSize);
}
Ricordiamo che il metodo getElementsByTagName
restituisce un array di nodi, e per tale motivo
accediamo al primo indice per prelevare il cor-
retto valore (il DOM non tiene in considerazione
il fatto che alcuni elementi definiti nell'HTML
dovrebbero essere unici).
CAMBIARE
L'USER AGENT
A volte capita di imbattersi in pagine in cui viene
impedito l'accesso a browser non compatibili, o
per semplici motivi etici: questa operazione di
selezione avviene identificando la stringa inviata
durante le richieste effettuate tramite il protocol-
lo HTTP da ogni browser. Tale voce dovrebbe
(User agents SHOULD include this field with
requests: citando la documentazione ufficiale del
protocollo) fornire informazioni valide sul pro-
prio software di navigazione. Poich il compo-
nente mx:html permette di modificarlo, faremo
in modo di variarla a nostro piacimento (opera-
zione possibile anche in Internet Explorer e
Firefox, in genere tramite l'utilizzo di plug-in).
Ho fornito alcune stringhe molto comuni, ma
sarebbe possibile utilizzare anche "Io
Programmo Browser - Leganza v1.2.08"! La clas-
se URLRequestDefaults, oltre a fornire tale funzio-
nalit, consente di decidere se gestire i cookies,
se seguire i redirect, se utilizzare la cache e di
impostare l'autenticazione per una certa con-
nessione.
URLRequestDefaults.userAgent = "Firefox
(WindowsXP) - Mozilla/5.0 (Windows; U; Windows NT
5.1; en-GB; rv:1.8.1.6) Gecko/20070725
Firefox/2.0.0.6"
Questo una delle possibili stringhe, ma si
potrebbe anche dichiarare di essere un web spi-
der (conosciuti anche come search bot) di un
motore di ricerca (Google, MSN Live, Yahoo ad
esempio), quei software che instancabilmente
analizzano milioni di pagine giornalmente per
aggiornare e popolare i database utilizzati nelle
ricerche dei contenuti disponibili nel web che
tutti effettuano:
URLRequestDefaults.userAgent = "Google -
Googlebot/2.1 (
http://www.googlebot.com/bot.html)"
MODALIT FULL SCREEN
Per passare dalla modalit fullscreen a quella
normale (e viceversa) baster semplicemente
utilizzare l'oggetto stage (non necessario
crearlo perch gi disponibile di default) e
verificare in quale dei due stati ci troviamo (per
velocit abbiamo utilizzato l'operatore terna-
rio); la modalit FULL_SCREEN_INTERACTIVE
permette id utilizzare la tastiera, funzionalit
non abilitata nella modalit FULL_SCREEN
(con la quale potreste addirittura scollegare la
tastiera e obbligare a navigare utilizzando solo il
mouse, evitando ulteriori modifiche o tentativi
di utilizzo improprio)
(stage.displayState==StageDisplayState.FULL_SCREE
N_INTERACTIVE)?
stage.displayState=StageDisplayState.NORMAL:
stage.displayState =
StageDisplayState.FULL_SCREEN_INTERACTIVE;
IN CONCLUSIONE
Per non complicare il codice abbiamo scelto di
mantenere alcune porzioni di codice all'interno
del componente personalizzato che stato crea-
to: questo genera, ovviamente, un certo dispen-
dio di memoria dovuta alla ridondanza di svaria-
te sezioni di codice quando si aggiungono i tab;
una sua ottimizzazione non comporterebbe
comunque grande sforzo.
Si potrebbero disabilitare anche le animazioni
Flash nel sito, oltre che evitare di visualizzare i
contenuti dei tag iframe e tante altre modifiche
strutturali/estetiche.
Spero anche questa volta di aver mostrato al let-
tore come ormai sia semplice realizzare soluzio-
ni ad hoc per le proprie necessit con il minimo
sforzo. Buona programmazione.
Andrea Leganza
COVER STORY M
G
18
/Novembre 2008
Browser fai da te! Come Chrome pi di Chrome
LAUTORE
Laureato in Ingegneria
Informatica, da oltre un
decennio realizza soluzioni
multimediali e software su
diverse piattaforme e
linguaggi. Certificato EUCIP
Core, appassionato di
fotografia, lingua
giapponese, tiro con l'arco,
istruttore di nuoto FIN,
attualmente svolge incarico
di Project Manager in un
progetto di ricerca che
utilizza agenti intelligenti;
contattabile su
neogene@tin.it o
direttamente sul sito
www.leganza.it
012-018:032-035 30-09-2008 16:07 Pagina 18
ht t p: / / www. i opr ogr ammo. i t
COVER STORY M
G
20
/Novembre 2008
Programmare applicazioni per il mondo mobile
G
oogle Google Google. Mai come in que-
stultimo periodo il nome del gigante di
Mountain View ha riecheggiato per i quat-
tro angoli del globo. Dopo lenorme successo del
suo motore di ricerca, Google ha letteralmente
invaso numerosi altri settori del mondo IT. Si
cominciato con il rivoluzionario servizio di posta
Gmail, con Google Maps e con Google News; si
proseguito con YouTube e le altre piattaforme Web
2.0; si arrivati infine al lancio del browser
Chrome. Dopo unesperienza di questo tipo, BigG
non poteva certo farsi sfuggire una gallina dalle
uova doro come il settore degli smartphone. Un
mercato, quello dei dispositivi portatili, gi recen-
temente scosso dalla calata di Apple e del suo
iPhone. La risposta di Google c, e si chiama
Android.
COS ANDROID
L dove Apple, forte del successo di iPod, ha scelto
di puntare su un device esclusivo con del software
proprietario, Google ha optato per una soluzione
diametralmente opposta. Alla chiusura e allesclu-
sivit di iPhone, BigG ed i suoi alleati propongono
una soluzione aperta e flessibile, attingendo a
piene mani dal mondo Open Source. Il gigante di
Mountain View, per il momento, non ha alcuna
intenzione di lanciare un vero e proprio googlefo-
nino. Android non un device hardware. Android
un sistema operativo per smartphone di ultima
generazione. LS.O. di Google si mette in concor-
renza diretta con Symbian OS (Nokia, Sony
Ericsson ed altri), Windows Mobile (Microsoft) e
naturalmente con il gi citato iPhone di Apple ed il
suo sistema basato su Mac OS X.
Nel momento in cui questo articolo viene scritto, in
commercio non ancora possibile trovare dei
dispositivi equipaggiati con Android. I primi, tutta-
via, sono oramai alle porte. Il pioniere HTC, che
ha promesso il lancio di un dispositivo Android tra
la fine del 2008 ed i primi mesi del 2009. Detta cos,
sembra che Android sia solo chiacchiere e distinti-
vo. E invece Android gi pu essere considerato
una piattaforma consolidata e ricca di applicazio-
ni. Google ha scelto di mettere a disposizione i kit
di sviluppo e le specifiche della piattaforma prima
ancora che questa sia sul mercato. Poco tempo fa
sono stati premiati i vincitori del primo Android
Developers Challenge, un concorso per program-
matori Android: gli sviluppatori che hanno inviato
le migliori applicazioni hanno ricevuto cospicue
somme di denaro. Evidentemente Google ed i suoi
alleati intendono arrivare sul mercato con un pro-
dotto stabile, dotato sin da subito di un ampio
parco applicativo.
LARCHITETTURA
DI RIFERIMENTO
Android, in tutti gli strati della sua architettura,
deve moltissimo al mondo Open Source e alle sue
comunit di sviluppatori. Anche lesperienza di
Java stata fondamentale durante il design dellar-
chitettura di riferimento.
Il sistema Android basato su un kernel Linux, ver-
sione 2.6. Direttamente nel kernel sono inseriti i
driver per il controllo dellhardware del dispositivo:
driver per la tastiera, lo schermo, il touchpad, il Wi-
Fi, il Bluetooth, il controllo dellaudio e cos via.
Conoscenze richieste
Java
Software
Java SDK 5+, Eclipse
3.3+
Impegno
Tempo di realizzazione
REQUISITI
ALLA SCOPERTA
DI GOOGLE ANDROID
ANDROID, LA PIATTAFORMA DI GOOGLE PER GLI SMARTPHONE DELLA PROSSIMA GENERAZIONE,
PROMETTE DI RIVOLUZIONARE LINTERO SETTORE. NON FACCIAMOCI COGLIERE IMPREPARATI:
INSTALLIAMO IL KIT DI SVILUPPO, EMULIAMO LA PIATTAFORMA E INIZIAMO A SVILUPPARE
Fig. 1: Larchitettura di Android
020-026:032-035 30-09-2008 16:22 Pagina 20
Sopra il kernel poggiano le librerie fondamentali,
tutte mutuate dal mondo Open Source. Ce n per
tutti i gusti. Da citare sono senzaltro OpenGL, per
la grafica, SQLite, per la gestione dei dati, e WebKit,
per la visualizzazione delle pagine Web.
Larchitettura prevede, poi, una macchina virtuale
e una libreria fondamentale che, insieme, costitui-
scono la piattaforma di sviluppo per le applicazio-
ni Android. Questa macchina virtuale si chiama
Dalvik, e sostanzialmente una Java Virtual
Machine. Come verificheremo pi tardi, alcune
delle caratteristiche di Dalvik e della sua libreria
non permettono di identificare immediatamente la
piattaforma Java disponibile in Android con una di
quelle di riferimento (Java SE, Java ME).
Nel penultimo strato dellarchitettura possibile
rintracciare i gestori e le applicazioni di base del
sistema. Ci sono gestori per le risorse, per le appli-
cazioni installate, per le telefonate, il file system e
altro ancora: tutti componenti di cui difficilmente
si pu fare a meno.
Infine, sullo strato pi alto dellarchitettura, pog-
giano gli applicativi destinati lutente finale. Molti,
naturalmente, sono gi inclusi con linstallazione
di base: il browser e il player multimediale sono dei
facili esempi. Allo stesso livello, per, possibile
installare qualsiasi altra applicazione lutente desi-
deri, anche sviluppata in casa, senza alcun control-
lo preventivo da parte di Google e soci.
INSTALLAZIONE
DEL KIT DI SVILUPPO
Per sviluppare applicazioni che siano in grado di
girare su sistemi Android, necessario installare sul
proprio PC un apposito kit di sviluppo, che sia
completo di emulatore, librerie e documentazione.
Se avete gi sviluppato per piattaforme quali Java
ME o Windows Mobile capite bene cosa intendo
(ma se non lavete mai fatto, non vi preoccupate:
qui si spiega tutto). La prima buona notizia, che
strapper un sorriso di soddisfazione a molti di voi,
che lAndroid SDK disponibile gratuitamente e
senza discriminazioni per sistemi Windows, Linux
e Mac. Come inizio, non c male.
possibile scaricarlo dalla home page del progetto,
allindirizzo http://code.google.com/android/.
Procedete pure al download del pacchetto adatto al
vostro sistema. Al momento della stesura di questo
articolo la pi recente versione disponibile la 0.9-
beta, ma se ne trovate di pi recenti fate pure: non
dovrebbero differire troppo da quella presa qui a
riferimento.
Linstallazione del kit veramente semplice.
Lunica cosa di cui bisogna accertarsi, prima di pro-
cedere, di soddisfare i requisiti di base. In partico-
lare, richiesto che il sistema disponga gi di un
Java SDK (JDK) versione 5 o successiva. stretta-
mente indispensabile soddisfare questo requisito,
poich Android si programma in Java, e senza un
JDK non possibile compilare il codice.
Dopo aver verificato i requisiti, possibile procede-
re. Prendete larchivio ZIP scaricato da Internet e
scompattatelo dove meglio preferite. tutto:
lAndroid SDK gi pronto alluso! Al massimo si
pu perfezionare linstallazione aggiungendo alla
variabile dambiente PATH del sistema operativo il
percorso della cartella tools che allinterno
dellAndroid SDK. Cos facendo sar pi semplice
invocare gli eseguibili del kit da riga di comando.
Loperazione, ad ogni modo, non indispensabile
per un corretto funzionamento del kit.
La prima cosa da fare, prendere confidenza con
Android. Entrate nella directory tools e avviate
leseguibile emulator. Qualche istante di pazienza
(al primo lancio anche un paio di minuti) e
lemulatore caricher e render disponibile il siste-
ma Android, in tutto il suo splendore. Con il mouse
possibile simulare il touchpad del dispositivo,
cliccando sullo schermo. Fatevi un giro e prendete
pure confidenza con lambiente.
Come prima cosa potete divertirvi con le applica-
zioni di base, come il browser o la rubrica: vi aiute-
ranno molto nel comprendere i principi di utilizzo
del sistema. Poi potete passate a del materiale pi
tecnico: il menu principale contiene le voci API
Demos e Dev Tools, che sono state inserite appo-
ht t p: / / www. i opr ogr ammo. i t
M
COVER STORY
Novembre 2008/
21
G
Programmare applicazioni per il mondo mobile
NOTA
OPEN HANDSET
ALLIANCE
Per essere precisi, dietro
Android non c soltanto
Google. Il colosso di
Mountain View ha fatto la
prima mossa e di sicuro
lattore di maggior peso,
tuttavia levoluzione di
Android curata da un
consorzio denominato
Open Handset Alliance.
Del gruppo, oltre a Google,
fanno parte numerosi altri
nomi interessanti, tra cui
HTC (che sar la prima a
produrre dispositivi
equipaggiati con Android),
Intel, Motorola, Samsung,
LG e molti altri. C anche
Telecom Italia.
Fig. 2: La schermata principale di un sistema Android
020-026:032-035 30-09-2008 16:22 Pagina 21
ht t p: / / www. i opr ogr ammo. i t
COVER STORY
M
G
22
/Novembre 2008
Programmare applicazioni per il mondo mobile
sitamente per chi Android vuole programmarlo, e
non solo farci un giro di prova.
INSTALLAZIONE DEL
PLUG-IN DI ECLIPSE
Bench lAndroid SDK disponga di script che auto-
matizzano linstallazione delle applicazioni, il lan-
cio dellemulatore ed il debug del codice, lavorare
in un ambiente integrato, con ogni opzione a por-
tata di clic, sicuramente pi facile. Specie quan-
do lambiente integrato si chiama Eclipse. Nel sito
di Android contattato in precedenza disponibile
anche un plug-in per la celebre piattaforma di svi-
luppo Open Source. Il modulo, al momento della
stesura di questo articolo, funziona con le pi
recenti versioni di Eclipse, che sono la 3.3 e la 3.4.
Pu essere installato direttamente dallinterno
della piattaforma di sviluppo.
Avviate Eclipse ed eseguite il wizard per
linstallazione di nuovi componenti (voce di menu
Help Software Updates nella versione 3.4, sele-
zionando poi la scheda Available software, oppu-
re voce di menu Help Software Updates Find
and install in Eclipse 3.3, scegliendo poi Search
for new features to install). Giunti a destinazione,
scegliete lopzione per aggiungere un nuovo sito
remoto alla lista delle fonti presentate dal wizard.
Lindirizzo da specificare :
https://dl-ssl.google.com/android/eclipse/
A questo punto selezionate la voce corrispondente
alla nuova fonte e procedete attraverso i singoli
passi del wizard. Il plug-in per lo sviluppo del
software Android sar automaticamente scaricato
ed installato.
Dopo il riavvio di Eclipse, recatevi nella schermata
delle preferenze dellambiente (voce di menu
Window Preferences). Qui troverete disponibile
la nuova categoria Android, nellelenco sulla sini-
stra. Selezionatela ed impostate il percorso del
vostro Android SDK: necessario affinch Eclipse
possa agganciare il kit di sviluppo. Durante questa
fase dovreste anche ricevere un pop-up per
laccettazione della licenza del plug-in.
Per ora possibile tralasciare le altre possibili
impostazioni collegate al plug-in: imparerete a
usarle pi avanti, quando avrete preso confidenza
con lambiente.
CIAO, MONDO
ANDROIDE!
Una trattazione rigorosa e didatticamente corretta
prevedrebbe, ora, lenunciazione dei principi di
programmazione di Android e lapprofondimento
della sua architettura software. Siete l, trepidanti,
con il dito pronto a lanciare lemulatore, ansiosi di
vedere del codice, anche per il pi banale degli
scopi? Cos sia! Rimandiamo pure la nostra rigoro-
sa trattazione teorica e lasciamoci andare ad uno
scontato ma liberatorio Ciao, Mondo!. Versione
Android, naturalmente!
Avviate Eclipse. Grazie al plug-in appena installato
disponete ora di una nuova categoria di progetto,
chiamata Android Project. Create un progetto di
questo tipo.
Nel wizard di creazione del progetto utilizzate la
seguente configurazione:
G Project name: CiaoMondoAndroide
G Package name: it.ioprogrammo.helloandroid
G Activity name: CiaoMondoAndroide
G Application name: Ciao Mondo
Il progetto, a questo punto, pu essere creato, azio-
nando il tasto Finish.
Eclipse popoler automaticamente il progetto,
inserendo le librerie di Android e la struttura di
base dei progetti per questa piattaforma. In uno
slancio di generosit, Eclipse provveder anche
alla creazione della prima classe della soluzione,
chiamata CiaoMondoAndroide (come specificato
alla voce Activity name) ed inserita nel pacchetto
it.ioprogrammo.helloandroid (come alla voce
Fig. 4: Affinch il plug-in funzioni correttamente
necessario fornire il percorso dellAndroid SDK, in
modo che questultimo possa essere agganciato
da Eclipse
Fig. 3: Configurando la nuova fonte in Eclipse,
possibile scaricare e installare automaticamente il
plug-in per lo sviluppo del software Android
NOTA
LE MIGLIORI
APPLICAZIONI
PER ANDROID
Volete sapere quali
applicazioni hanno fruttato
premi ai loro creatori, al
termine del primo Android
Developer Challenge?
Puntate il vostro browser
allindirizzo
http://code.google.com/a
ndroid/adc_gallery/
020-026:032-035 30-09-2008 16:22 Pagina 22
ht t p: / / www. i opr ogr ammo. i t
Package name). Aprite il codice della classe e
modificatelo alla seguente maniera:
package it.ioprogrammo.helloandroid;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class CiaoMondoAndroide extends Activity {
public void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("Ciao, Mondo
Androide!");
setContentView(tv);
}
}
Ora cliccate il tasto di lancio nella toolbar di
Eclipse (oppure azionate la voce di menu Run
Run). Nella finestra pop-up che sar aperta, sce-
gliete la modalit di avvio Android Application.
Lemulatore verr caricato. Eclipse provveder
automaticamente a installare al suo interno
lapplicazione CiaoMondoAndroide, per poi
avviarla non appena loperazione sar completata.
fatta: il vostro primo software per Android sta
girando davanti ai vostri occhi.
Successivamente, accedendo alle configurazioni
di esecuzione (voce di menu Run Run
Configurations in Eclipse 3.4, Run Open Run
Dialog in Eclipse 3.3), sar possibile alterare i
parametri di avvio dellemulatore e dellapplica-
zione. Vi consiglio di fare qualche esperimento.
Provate, ad esempio, a collaudare il software con le
differenti skin (e le diverse proporzioni dello
schermo) selezionabili tra le impostazioni della
configurazione di lancio. Un altro esperimento
interessante, che vi consiglio di compiere prima di
procedere oltre, lutilizzo del debugger di Eclipse
con lapplicazione Android. Ponete un breakpoint
sulla classe realizzata ed avviate di nuovo emulato-
re ed applicazione, questa volta in modalit debug.
DALVIK
E LE LIBRERIE ANDROID
Superata lesaltazione per il nostro primo progetto
Android, torniamo ad occuparci seriamente dei
concetti fondamentali per la programmazione in
questo ambiente. Come abbiamo appreso e dimo-
strato, la piattaforma di sviluppo di natura Java.
Tuttavia si tratta di una piattaforma particolare e
personalizzata, che vale la pena approfondire.
La macchina virtuale, chiamata Dalvik, sembra
essere una Java Virtual Machine, ma in realt non
lo del tutto. Mi spiego meglio: una Java Virtual
Machine esegue del codice bytecode, giusto? Ecco,
la Dalvik Virtual Machine non esegue bytecode
standard, ma un altro linguaggio, chiamato DEX
(Dalvik EXecutable), studiato appositamente per
una migliore resa in uno smartphone.
Con lAndroid SDK ed Eclipse, ad ogni modo, ci
sembrer di utilizzare una regolare Java Virtual
Machine. Lambiente di sviluppo, infatti, provvede
automaticamente alla generazione del codice
DEX, ri-compilando il bytecode che a sua volta
frutto di una prima comune compilazione Java.
Per noi sar tutto trasparente. Questa peculiarit
di Dalvik, quindi, non influenzer il nostro modo
di programmare.
La stessa considerazione, invece, non pu essere
fatta riguardo la libreria di base che affianca
Dalvik. Aprite il documento al percorso
docs/reference/ packages.html, nel vostro Android
SDK. lindice dei package Java compresi nella
libreria di base. Scorretela velocemente e traete
pure le prime conclusioni. C parecchio della
Standard Edition di Java, ma non c tutto.
Ad esempio non ci sono AWT e Swing. I pacchetti
fondamentali, per, ci sono tutti, ed appaiono in
larga misura identici a come li vuole Sun. Davvero
poco viene dalla Micro Edition, praticamente
nulla. La piattaforma Java ME stata snobbata da
M
COVER STORY
Novembre 2008/
23
G
Programmare applicazioni per il mondo mobile
Fig. 5: Eclipse mette a disposizione lopzione di
avvio Android Application
Fig. 6: Lapplicazione CiaoMondoAndroide, ese-
guita dallemulatore
NOTA
DOCUMENTAZIONE
Molta documentazione
(soltanto in inglese, per)
messa a disposizione nella
cartella docs dellAndroid
SDK
Fig. 5: Volt
020-026:032-035 30-09-2008 16:22 Pagina 23
ht t p: / / www. i opr ogr ammo. i t
Android, che le ha preferito una libreria pi simile
a quella di un sistema desktop. Non passano poi
inosservati i tanti package con prefisso android
che, naturalmente, sono esclusivi di questa specia-
le piattaforma. Servono per linterazione diretta
con le funzionalit del sistema sottostante.
Ad esempio: il package android.widget contiene i
componenti custom di Android per la costruzione
delle interfacce grafiche (in CiaoMondoAndroide
abbiamo usato TextView); nel pacchetto
android.graphics ci sono le funzioni primitive per
la grafica di pi basso livello; in android.location ci
sono gli strumenti per interagire con un eventuale
ricevitore GPS compreso nel dispositivo. Ciascuno
dei pacchetti android, naturalmente, meriterebbe
una trattazione estesa e completa, tanti sono i pos-
sibili campi di applicazione. Ne emerge il profilo di
una piattaforma di sviluppo complessa, perch
molto ricca, ma semplice, perch ordinata e perch
condivide parecchio con ledizione tradizionale di
Java.
Il consiglio, naturalmente, quello di tenere sem-
pre a portata di mano la documentazione delle API
di Android. Fatevi poi guidare dalla curiosit: date
pure una prima occhiata alle classi che pi stuzzi-
cano la vostra fantasia.
PRINCIPI
DI PROGRAMMAZIONE
Chi programma con Java ME sa che le MIDlet sono
il mattone fondamentale delle applicazioni MIDP;
chi crea applicazioni Web con Java EE non pu
ignorare cosa sia una Servlet; persino i programma-
tori meno esperti sanno che le applicazioni Java,
per girare in un browser, devono essere inglobate in
una Applet. Tutto questo per dire che ciascun
ambiente, Java e non, dispone dei suoi mattoni
fondamentali, che lo sviluppatore pu estendere e
implementare per trovare un punto di aggancio
con la piattaforma. Android non sfugge alla regola,
anzi la amplifica. A seconda di quel che si intende
fare disponibile un diverso modello. Android for-
nisce quattro mattoni di base:
G Attivit
Le attivit sono quei blocchi di unapplicazione
che interagiscono con lutente utilizzando lo
schermo e i dispositivi di input messi a disposi-
zione dallo smartphone. Comunemente fanno
uso di componenti UI gi pronti, come quelli
presenti nel pacchetto android.widget, ma que-
sta non necessariamente la regola.
Lapplicazione dimostrativa CiaoMondoAndroi -
de unattivit. Le attivit sono probabilmente il
modello pi diffuso in Android, e si realizzano
estendendo la classe base android.app.Activity.
G Servizio
Un servizio gira in sottofondo, in un suo thread,
e non interagisce direttamente con lutente.
Ad esempio pu riprodurre un brano MP3, men-
tre lutente utilizza delle attivit per fare altro.
Un servizio si realizza estendendo la classe
android.app.Service.
G Broadcast Intent Receiver
Un Broadcast Intent Receiver viene utilizzato
quando si intende intercettare un particolare
evento, attraverso tutto il sistema. Ad esempio lo
si pu utilizzare se si desidera compiere
unazione quando il telefono squilla o quando si
riceve un messaggio. La classe da estendere
android.content.BroadcastReceiver.
G Content Provider
I Content Provider sono utilizzati per esporre dati
e informazioni. Costituiscono un canale di
comunicazione tra le differenti applicazioni
installate nel sistema. Si pu creare un Content
Provider estendendo la classe astratta android. -
content.ContentProvider.
Unapplicazione Android costituita da uno o pi
di questi elementi. Molto frequentemente, contie-
ne almeno unattivit, ma non detto che debba
sempre essere cos.
I PACCHETTI APK
Le applicazioni Android sono distribuite sotto
forma di file APK(Android Package). Al loro interno
vengono raccolti gli eseguibili in formato DEX, le
eventuali risorse associate e una serie di descrittori
che delineano il contenuto del pacchetto. In parti-
colare, nel cosiddetto manifesto, vengono dichiara-
te le attivit, i servizi, i provider e i receiver compre-
si nel pacchetto, in modo che il sistema possa
agganciarli ed azionarli correttamente.
Torniamo, in Eclipse, sul progetto CiaoMondo -
Androide. Al suo interno troverete un file chiamato
AndroidManifest.xml, fatto come segue:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.
android.com/apk/res/android"package="it.ioprogrammo
.helloandroid">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".CiaoMondoAndroide"
android:label="@string/app_name">
<intent-filter><action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
COVER STORY
M
G
24
/Novembre 2008
Programmare applicazioni per il mondo mobile
NOTA
IL TOOL ADB
Oltre allemulatore, la
cartella tools dellAndroid
SDK contiene un altro
strumento molto
interessante, chiamato
adb. Si utilizza da riga di
comando. Lanciatelo
senza parametri, ed avrete
un veloce aiuto in linea
sulle funzionalit messe a
disposizione.
In particolare, mentre
lemulatore in
esecuzione, i comandi adb
install e adb uninstall
possono essere utilizzati
per installare e rimuovere
applicazioni dal
dispositivo, mentre lo
speciale comando adb
shell permette di aprire
una shell sul sistema
Android emulato.
020-026:032-035 30-09-2008 16:22 Pagina 24
ht t p: / / www. i opr ogr ammo. i t
</intent-filter>
</activity>
</application>
</manifest>
il manifesto descrittore citato poco fa. Al suo
interno potete e dovete dichiarare i componenti
del vostro software. Eclipse, allatto di creazione del
progetto, ha gi eseguito su di esso alcune configu-
razioni iniziali. Ad esempio ha registrato lattivit
CiaoMondoAndroide, ha specificato le propriet
generali dellapplicazione e ha anche generato
edimpostato unicona per il nostro programma
(res/drawable/icon.png). Ovviamente queste scelte
possono essere alterate, e nuovi componenti pos-
sono essere aggiunti al progetto. Con lo speciale
editor visuale messo a disposizione da Eclipse, vi
risulter tutto molto semplice: sufficiente fare un
po di pratica e approfondire, di volta in volta,
laspetto dinteresse.
Una volta che il lavoro stato completato, possi-
bile esportare il file APK da distribuire ai fortunati
possessori di un sistema Android. In Eclipse, anco-
ra una volta, questione di un clic: aprite il menu
contestuale sulla radice del progetto (tasto destro
del mouse, in Windows) e selezionate la voce
Android Tools Export Unsigned Application
Package.
Prima di distribuire in giro il pacchetto necessario
apporre su di esso una firma digitale. In caso con-
trario, Android non potr installarne i contenuti.
Questo lunico vincolo imposto dal sistema. Il
fatto che un pacchetto debba essere firmato non
deve preoccupare lo sviluppatore: non necessario
che una certification authority riconosca la chiave
utilizzata per la firma. Di conseguenza possibile
firmare un pacchetto APK anche servendosi di un
certificato fatto in casa. In parole semplici: non
bisogna pagare nessuno perch i nostri software
siano autorizzati, possiamo fare tutto da noi. Il JDK
comprende tutti gli strumenti necessari per gene-
rare una chiave e utilizzarla per apporre una firma
digitale. Se non avete mai sperimentato questo
processo (che anche altri tipi di componenti Java
richiedono), non vi dovete preoccupare: spiegher
immediatamente come procedere. necessario
ricorrere alla riga di comando. Aprite il prompt e
raggiungete la cartella bin del vostro JDK (se questa
fa parte del PATH di sistema, non necessario
entrare nella directory). Per prima cosa bisogna
generare una chiave. Lo si pu fare con lo strumen-
to keytool. Invocate il comando:
keytool -genkey -alias miachiave
Lo strumento vi chieder un po di informazioni
che dovete impostare da voi, come la password per
accedere allutilizzo della chiave generata (ricorda-
tevela, poi!) e le informazioni anagrafiche da inseri-
re al suo interno. Rispondete ad ogni domanda.
Al termine del processo il vostro computer disporr
di una chiave, chiamata miachiave. Ora possibile
utilizzare la chiave generata per firmare un pac-
chetto APK. Lo strumento da invocare si chiama
jarsigner. Come il nome lascia intendere, jarsigner
stato concepito per firmare i pacchetti JAR di Java.
Data la vicinanza tra i due standard, comunque
possibile usarlo anche con un file APK di Android.
Sempre da riga di comando, richiamate il coman-
do:
jarsigner C:\CiaoMondoAndroide.apk miachiave
Naturalmente, al percorso C:\CiaoMondoAndroi -
de.apk, dovete sostituire la posizione che ha il file
APK nel vostro sistema. La chiave miachiave sar
caricata (vi verr richiesta la password impostata in
precedenza) ed il file sar firmato. Missione com-
piuta! Lapplicazione pronta per la grande distri-
buzione.
UN ALTRO ESEMPIO:
SMSAUTORESPONDER
Torniamo sul codice ed esaminiamo un esempio
un po pi significativo, che ci permetter di cono-
scere alcuni altri aspetti della piattaforma Android
e del suo kit di sviluppo. Realizziamo un software
chiamato SMSAutoResponder, unap plicazione in
grado di rispondere automaticamente agli SMS in
arrivo. Lutente deve poter specificare il testo della
risposta automatica e deve anche poter attivare e
disattivare la funzione quando vuole.
In Eclipse creiamo un progetto Android, impostan-
do le seguenti propriet iniziali:
M
COVER STORY
Novembre 2008/
25
G
Programmare applicazioni per il mondo mobile
NOTA
ALTRE RISORSE
(XML E NON)
Oltre alle stringhe,
memorizzate in
res/values/strings.xml, e le
interfacce, inserite in
res/layout, esistono altri
tipi di risorse che le
applicazioni possono
utilizzare passando per la
classe R. Dentro
res/drawable, ad esempio,
ci sono le immagini;
dentro res/anim
possibile inserire dei file
XML che specificano delle
animazioni grafiche; in
res/values/colors.xml
possibile definire un set di
colori; in
res/values/dimens.xml va
un set di dimensioni; infine
in res/values/styles.xml
possibile definire stili e
temi per laspetto grafico
del software.
Per approfondire questi
temi fate riferimento alla
documentazione a corredo
dellSDK, al percorso
docs/reference/available-
resources.html.
Fig. 7: Lo speciale editor messo a disposizione da
Eclipse per il file AndroidManifest.xml
020-026:032-035 30-09-2008 16:22 Pagina 25
ht t p: / / www. i opr ogr ammo. i t
G Project name: SMSAutoResponder
G Package name:
it.ioprogrammo.smsautoresponder
G Activity name: SMSActivity
G Application name: SMS Auto Responder
Eclipse preparer la struttura di base, come gi
aveva fatto prima per CiaoMondoAndroide.
Poco fa si accennato al file AndroidManifest.xml,
che il principale descrittore di unapplicazione
Android, ma non comunque lunico. Grazie a
questo nuovo esperimento conosceremo un altro
paio di descrittori.
Eclipse, allinterno del progetto, ha predisposto un
file al percorso res/values/strings.xml. Questo file
pu essere utilizzato per dichiarare i messaggi
testuali utilizzati dallapplicazione. In questo modo
non necessario scrivere nel codice (cablare, come
si dice in gergo) tutte le stringhe che linterfaccia
mostra allutente, come i messaggi di avviso o le
etichette dei bottoni. un bel beneficio. Anzitutto
perch possibile manipolare i messaggi senza
alterare il codice, e poi perch le strutture di questo
tipo rendono pi semplice linternazionalizzazione
delle applicazioni. SMSAutoResponder utilizzer
pochi messaggi. Andiamo a modificare strings.xml
in modo che il suo contenuto sia:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">SMS Auto
Responder</string>
<string name="message">Messaggio da
inoltrare:</string>
<string name="activate">Attiva</string>
<string name="deactivate">Disattiva</string>
<string name="textError">Specificare un
testo per la risposta automatica</string>
</resources>
Con questa struttura abbiamo definito cinque
diversi messaggi. Dopo aver salvato il file, lanciate
unoperazione di build esplicita con Eclipse (voce
di menu Project Build Project o Project Build
All). Nel pacchetto it.ioprogrammo.smsauto -
responder verr creata, o aggiornata se esisteva gi,
una classe chiamata semplicemente R. Si tratta di
una facilitazione messa a disposizione dal kit di svi-
luppo, utile per richiamare da codice quel che si
specificato nei descrittori XML dellapplicazione.
In particolare, ai messaggi di strings.xml vengono
associati degli indici numerici, raggiungibili come
propriet statiche della classe R.string. Parecchi
metodi della API di Android accettano questa tipo-
logia di identificativi. Ad esempio, possibile utiliz-
zare il messaggio Attiva (nome nel file XML: acti-
vate) come etichetta di un bottone, con
unistruzione del tipo:
button.setText(R.string.activate);
A proposito di interfacce utente: avete mai provato
a svilupparne su altri ambienti Java, magari con
Swing? Le librerie grafiche di questo genere sono
molto potenti, ma hanno lo svantaggio di richiede-
re tanto codice. Sono noiose, per farla breve. Ogni
volta che si vuole realizzare una GUI, anche mini-
male, c parecchio da scrivere: crea il pannello,
scegli la metodologia per disporre i componenti,
dichiara i componenti, crea i componenti, imposta
le propriet dei componenti, aggiungi i componen-
ti e altre mille operazioni a non finire. Le interfacce
Web, da questo punto di vista, sono pi agevoli:
HTML e XML non sono linguaggi di programma-
zione, ma sono molto espressivi quando devono
descrivere una GUI. molto pi facile descrivere
uninterfaccia grafica con XML piuttosto che con
Java. Limportante che, a disegno ultimato, la GUI
possa essere richiamata da un programma vero e
proprio che ne esegua la logica. Ecco, Android pre-
vede proprio un approccio di questo tipo.
Date uno sguardo al percorso di progetto
res/layout. In questa cartella possibile posizionare
dei file XML in grado di descrivere uninterfaccia
grafica. Eclipse ne ha creato uno predefinito, chia-
mato main.xml. Modifichiamolo come segue:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.
android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/message" />
<EditText android:id="@+id/textField1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/activate" />
</LinearLayout>
Abbiamo dichiarato uninterfaccia molto semplice,
formata da tre componenti, uno sotto laltro.
Il primo, di tipo TextView, mostra un testo che :
@string/message
Questa formula indica ad Android di andare nel file
con le stringhe (lo strings.xml di poco fa) e di prele-
vare dal suo interno il testo con nome message.
Chiudendo il giro, questo TextView mostrer la
stringa Messaggio da inoltrare:.
Segue un componente EditText. Si tratta di un
COVER STORY M
G
26
/Novembre 2008
Programmare applicazioni per il mondo mobile
NOTA
IL TOOL ADB
La piattaforma di sviluppo
di Android dispone di
metodi molto semplici per
rendere internazionali le
applicazioni. Ciascuna
delle sotto-cartelle della
directory res pu essere
ripetuta pi di una volta,
accodando al suo nome un
trattino e la sigla del paese
cui sono destinate le
risorse contenute al suo
interno. Ad esempio: se
voglio realizzare
unapplicazione in italiano
ed in inglese, posso
realizzare due strings.xml
distinti, applicando poi al
progetto una struttura del
tipo:
Progetto/res/values-en/
strings.xml values
it/strings.xml
020-026:032-035 30-09-2008 16:22 Pagina 26
ht t p: / / www. i opr ogr ammo. i t
campo di testo editabile, che lutente pu utilizza-
re per digitare un messaggio. Allattributo
android:id associato lo speciale valore:
@+id/textField1
Cos si crea un ID textField1 che, come vedremo a
breve, sar riferito nella classe R. Sar quindi possi-
bile utilizzarlo per recuperare e manipolare il com-
ponente da codice Java.
Ultimo elemento in lista un Button, cio un bot-
tone, al quale stato dato ID button1 ed etichetta
con stringa activate (Attiva).
Ancora una volta, eseguiamo la build del progetto.
La classe R si arricchir di nuovi elementi. Ci sar
un campo R.layout.main, che potremo utilizzare
per identificare linterfaccia descritta in main.xml.
Poi troveremo anche i valori R.id.button1 e
R.id.textField1. Li utilizzeremo per recuperare e
manipolare i componenti corrispondenti.
Finora ci siamo dati abbastanza da fare con i
descrittori XML. ora di passare ad un po di sano
codice Java! Cominciamo dallattivit SMSActivity,
il cui scheletro stato gi preparato da Eclipse, e
che noi andremo a completare al seguente modo:
package it.ioprogrammo.smsautoresponder;
import android.app.Activity;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.Editable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
/*** Questa attivit permette all'utente di specificare
un testo e di avviare un receiver che mander una
risposta automatica, con il testo specificato, a ogni SMS
ricevuto. */
public class SMSActivity extends Activity implements
OnClickListener {
/*** Flag di attivazione. */
private boolean activated = false;
/*** Bottone per attivare e disattivare la funzione.*/
private Button button;
/*** Il campo di testo dove l'utente inserisce il testo
della risposta automatica. */
private EditText textField;
/*** Il receiver che intercetta gli SMS in arrivo. */
private SMSReceiver receiver = new SMSReceiver();
/*** Richiamato per creare l'attivit. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Imposta il layout.
setContentView(R.layout.main);
// Risolve il bottone di attivazione/disattivazione.
button = (Button) findViewById(R.id.button1);
// Imposta il listener.
button.setOnClickListener(this);
// Risolve il campo di testo con il messaggio da inviare.
textField = (EditText) findViewById(R.id.textField1);
}
/** * Attiva la funzione di risposta automatica agli SMS.
*/
private void activate()
{
// Recupera il testo della risposta automatica.
Editable e = textField.getText();
String smsText = e.toString();
smsText = smsText.trim();
// Convalida il testo.
if (smsText.length() == 0) {
Toast toast = Toast.makeText(getApplicationContext(),
R.string.textError, Toast.LENGTH_SHORT);
toast.show();
return;
}
// Cambia la scritta nel bottone.
button.setText(R.string.deactivate);
// Disattiva il campo di testo.
textField.setEnabled(false);
// Imposta il testo sul receiver.
receiver.setSmsText(smsText);
// Registra il receiver.
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEI
VED");
registerReceiver(receiver, filter);
// Cambia il flag di attivazione.
activated = true; }
/*** Disattiva la funzione di risp. automatica agli SMS.
private void deactivate() {
// Cambia la scritta nel bottone.
button.setText(R.string.activate);
// Attiva il campo di testo.
textField.setEnabled(true);
// De-registra il receiver.
unregisterReceiver(receiver);
// Cambia il flag di attivazione.
activated = false; }
/*** Richiamato al clic sul bottone di
attivazione/disattivazione della funzione. */
public void onClick(View v) {
if (!activated) { activate();
} else { deactivate(); }}}
Cominciamo esaminando il metodo onCreate(),
che quello che viene richiamato alla creazione
dellattivit. Osservate listruzione:
setContentView(R.layout.main);
Con questo comando si chiede di caricare e
mostrare linterfaccia descritta in res/layout/main. -
xml. Subito sotto vengono recuperati i riferimenti
M
COVER STORY
Novembre 2008/
27
G
Programmare applicazioni per il mondo mobile
020-026:032-035 30-09-2008 16:22 Pagina 27
ht t p: / / www. i opr ogr ammo. i t
al bottone e al campo di testo editabile:
button = (Button) findViewById(R.id.button1);
textField = (EditText)
findViewById(R.id.textField1);
Sul bottone, inoltre, viene anche impostato un lis-
tener:
button.setOnClickListener(this);
SMSActivity, fateci caso, implementa linterfaccia
OnClickListener. Permette di utilizzarla come liste-
ner su un bottone o su un altro oggetto che genera
lo stesso tipo di evento. In parole semplici, quando
si clicca sul bottone il sistema provvede a eseguire
il metodo onClick() di SMSActivity. Il codice inseri-
to nel metodo dirotta lesecuzione verso activate()
o, in base al valore di una variabile booleana, verso
deactivate(). Il primo, naturalmente, serve ad atti-
vare la funzione di risposta automatica agli SMS in
arrivo. Al suo interno, activate() controlla che
lutente abbia specificato il testo di risposta. In caso
contrario mostra un avviso (attraverso la classe
android.widget.Toast) ed interrompe la procedura
di avvio. Al contrario, se il messaggio stato corret-
tamente specificato, si prosegue alla registrazione
di unistanza di SMSReceiver, una classe che andre-
mo a realizzare tra poco. Come il nome lascia ad
intendere, SMSReceiver sar un broadcast intent
receiver.
Questo genere di componenti, sono utilizzati per
intercettare gli eventi. Per dirla con il gergo di
Android, a dire il vero, dovremmo parlare di inten-
ti, non di eventi. La sostanza, per, la stessa,
almeno dal nostro attuale punto di vista. A noi inte-
ressa sapere quando il sistema riceve un SMS. Per
questo motivo listanza di SMSReceiver viene cos
registrata:
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_REC
EIVED"); registerReceiver(receiver, filter);
Con registerReceiver() si registra il receiver e lo si
abbina ad un filtro. In particolare si richiede di
intercettare lazione android.provider.Telephony. -
SMS_RECEIVED, che naturalmente corrisponde
alla ricezione di un SMS. Dopo aver registrato il
receiver, il metodo activate() di SMSActivity appor-
ta alcune modifiche allinterfaccia grafica, renden-
do non modificabile il campo di testo e cambiando
il messaggio nel bottone da Attiva in Disattiva.
Dopo aver modificato lo stato corrente (activated =
true), activate() conclude i propri compiti e termi-
na. Il metodo deactivate(), ovviamente, compie le
operazioni inverse: rimuove il receiver chiamando
unregisterReceiver(), riporta linterfaccia grafica al
suo stato iniziale ed appunta lo stato di inattivit
(activated = false). Passiamo a SMSReceiver. Come
insegnato qualche pagina dietro, un receiver va
realizzato estendendo la classe android.content.
BroadcastReceiver:
package it.ioprogrammo.smsautoresponder;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.provider.Telephony;
import android.telephony.gsm.SmsManager;
import android.telephony.gsm.SmsMessage;
import android.util.Log;
/*** Tale receiver pensato per ricevere una
notifica quando un c' un SMS in arrivo. Risponde al
messaggio in arrivo con un nuovomessaggio contenente
il testo impostato con il metodo setSmsText(). */
public class SMSReceiver extends BroadcastReceiver {
private String smsText;
/*** Imposta il testo degli SMS da inoltrare. *
* @param smsText - Il testo per gli SMS. */
public void setSmsText(String smsText) {
this.smsText = smsText;}
/*** Richiamato quando c' un SMS in arrivo. */
public void onReceive(Context context, Intent intent) {
// Recupera il messaggio o i messaggi arrivati.
SmsMessage[] messages = Telephony.Sms.Intents
.getMessagesFromIntent(intent);
// Per ciascun messaggio manda la risposta automatica.
for (SmsMessage msg : messages) {
String from = msg.getOriginatingAddress();
SmsManager manager = SmsManager.getDefault();
try {
PendingIntent i = PendingIntent.getActivity(context, 0,
new Intent(), PendingIntent.FLAG_CANCEL_CURRENT);
manager.sendTextMessage(from, null, smsText, i, null);
Log.i("SMSAutoResponder", "Risposta automatica
inviata a " + from);} catch (Throwable t) {
Log.e("SMSReceiver", "errore", t); }
} } }
Estendo BrodcastReceiver si deve implementare il
metodo onReceive(). Al suo interno vengono invo-
cate le funzionalit della libreria Android utili per
esaminare i messaggi ricevuti e per inoltrare le
risposte automatiche. Tutte questi strumenti di
dettaglio sono descritti nella documentazione di
Android SDK. Lesito delloperazione viene memo-
rizzata nel log di sistema, grazie ai metodi statici
della classe android.util.Log. Manca solo il
descrittore Android Manifest.xml, da aggiornare
cos:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.
android.com/apk/res/android"
COVER STORY M
G
28
/Novembre 2008
Programmare applicazioni per il mondo mobile
020-026:032-035 30-09-2008 16:22 Pagina 28
ht t p: / / www. i opr ogr ammo. i t
package="it.ioprogrammo.smsautoresponder">
<uses-permission
android:name="android.permission.RECEIVE_SMS" />
<uses-permission
android:name="android.permission.SEND_SMS" />
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".
SMSActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter> </activity> </application> </manifest>
Rispetto alla versione preconfezionata fornita da
Eclipse, in cui lattivit di base SMSActivity era gi
registrata, sono state aggiunte due importati righe:
<uses-permission android: name="android.
permission.RECEIVE_SMS" />
<uses-permission android:name="android.
permission.SEND_SMS" />
Con queste si dichiara che il software ha bisogno
dei permessi necessari per ricevere e inviare SMS.
Android pretende sempre che il descrittore dichia-
ri esattamente cosa fa il software, per gestire la
sicurezza dei dati e del sistema. Android non ci
impedisce di utilizzare le funzioni pi delicate, ma
ci impone di dichiararlo esplicitamente. In questo
modo lutente viene tutelato ed informato su cosa
faccia veramente ciascun software installato.
Se si omette la richiesta di un permesso, infatti, lo
stesso sar negato nel momento in cui loperazione
corrispondente dovr essere svolta.
Unapplicazione non dichiara di voler spedire SMS,
ma poi prova ad inviarne? Bene, il sistema glielo
impedir. tutto! Avviate pure lemulatore. A que-
sto punto impostate un messaggio di risposta auto-
matica e attivate la funzione, servendovi del botto-
ne attiva. Per sperimentare il software nella sua
totalit, necessario ricevere un SMS.
Lemulatore, chiaramente, non collegato ad una
rete GSM, e pertanto lidea di mandare un messag-
gio dal proprio cellulare bislacca.
Fortunatamente esistono diverse maniere per
simulare la ricezione di chiamate e SMS.
In Eclipse c una prospettiva chiamata DDMS.
Avviate lemulatore, poi richiamate la prospettiva
con la voce di menu Window Open Perspective
Other, scegliendo DDMS dallelenco delle pro-
spettive disponibili. Nella prospettiva DDMS avete
a portata di mano una console completa sugli
emulatori in esecuzione. Le diverse parti dello
schermo permettono di navigare il file system
emulato, di consultare i log, di verificare lo stato
della memoria e tantissime altre cose ancora.
In giro per lo schermo, di solito a sinistra, c anche
una scheda chiamata Emulator Control. Dal suo
interno possibile controllare alcuni aspetti delle-
mulatore, come ad esempio le coordinate restituite
dal suo finto ricevitore GPS. anche possibile inol-
trare SMS e chiamate al dispositivo. Scorrendo i
contenuti della scheda, localizzate il gruppo
Telephony Actions. Impostate un numero di
telefono qualsiasi nel campo Incoming number,
selezionate SMS, specificate un testo per il mes-
saggio ed azionate il tasto Send. Lemulatore tril-
ler e vi avviser del messaggio in arrivo. Nella
sezione LogCat di DDMS, dovreste trovare una
linea di log del tipo Risposta automatica inviata a,
seguita dal numero impostato come mittente.
Il problema, utilizzando DDMS, che dal log si
capisce che tutto ha funzionato correttamente, ma
non si capisce se la risposta automatica ok.
Vi insegno un trucco, valido sia per gli SMS che per
le chiamate. Dopo aver avviato lapplicazione
SMSAutoResponder nellemulatore lanciato da
Eclipse, andate nella cartella tools dellAndroid
SDK e lanciate leseguibile emulator, come quando
lo abbiamo provato la prima volta.
Dopo qualche istante avremo a disposizione due
sistemi Android, eseguiti simultaneamente ma
distintamente.
Bene, quello il numero di telefono del dispositivo
emulato. In pratica potete effettuare finte chiamate
e spedire finti SMS da un emulatore allaltro, utiliz-
zando i numeri riportati.
Leggete il numero dellemulatore lanciato da
Eclipse, quello in cui SMSAutoResponder in ese-
cuzione. Utilizzatelo ora nel secondo emulatore,
quello lanciato manualmente, per mandare un
SMS.
Potrete cos provare lebbrezza di ricevere e verifi-
care la risposta automatica.
Carlo Pelliccia
M
COVER STORY
Novembre 2008/
29
G
Programmare applicazioni per il mondo mobile
LAUTORE
Carlo Pelliccia lavora
presso la Software Factory
di Altran Italia, dove si
occupa di analisi e sviluppo
software per piattaforme
Java. Nella sua carriera di
technical writer ha
pubblicato cinque manuali
ed oltre centocinquanta
articoli, molti dei quali
proprio tra le pagine di
ioProgrammo. Il suo sito,
che ospita anche diversi
progetti Java Open Source,
disponibile allindirizzo
www.sauronsoftware.it
Fig. 8: La risposta giunta a destinazione
020-026:032-035 30-09-2008 16:22 Pagina 29
ht t p: / / www. i opr ogr ammo. i t
BIBLIOTECA M I migliori testi scelti dalla redazione
G
30
/Novembre 2008
PROGRAMMAZIONE IN JAVA
U
n testo che vuole essere al
contempo una guida al lin-
guaggio e alla programmazione
pi in generale. Il lettore viene gui-
dato alla scoperta degli elementi di
Java scoprendo al contempo la
potenza della logica orientata agli
oggetti. Molto efficace lapproccio
problem-oriented che spinge il
lettore verso lacquisizione di una
autonoma capacit risolutiva
rispetto alle sfide delle program-
mazione. Numerosi esercizi e piccoli progetti autonomi sono
di grande stimolo e offrono un piacevole diversivo durante
lapprendimento. evidente la volont degli autori di unire
eleganza ed efficacia nel codice. Il testo concepito per prin-
cipianti e fa riferimento alla versione 6 di Java. Come molti
manuale di questa fattura, permette due approcci: lo si pu
leggere in maniera sequenziale, dalla prima allultima pagina,
e in questo caso si avr la sensazione di seguire un ottimo
corso di programmazione Java; in alternativa il lettore pu
affrontare le singole sezioni del testo per colmare eventuali
lacune o approcciare tecniche e librerie che gli siano di speci-
fico interesse nello sviluppo di un progetto. Il testo di dimostra
completo anche nel descrivere gli elementi di base delle API
Java relative allinput/output, oltre a collection, reflection e
JavaBeans. Particolare attenzione rivolta alla programmazio-
ne concorrente, sia dal punto di vista teorico che implementa-
tivo: chi la ha affrontata sa bene le difficolt e le trappole in
cui facile cadere se non si guidati correttamente. Molto
ricco anche il CD, con software open source tutto dedicato allo
sviluppo.
Autori: Roberto Bruni, Andrea Corradini, Vincenzo Gervasi
Casa Editrice: Apogeo, Collana: Idee e Strumenti
Pagine: 432 ISBN: 9788850326617 Prezzo: 32,00 Euro
MICROSOFT ASP.NET AJAX
D
ino Esposito una specie di stel-
la nel firmamento della pro-
grammazione .NET, e a ragione.
La semplicit che riesce a raggiunge-
re con uno stile diretto e asciutto,
consente al lettore di entrare subito
in possesso delle nozioni pi utili
contenute nel libro. Lapproccio emi-
nentemente pratico sottolineato
dai numerosi esempi di codice che,
oltre a proporsi come snippet
pronti per essere utilizzati, consento-
no al lettore di familiarizzare con codice ben scritto. Il volume
diviso in tre sezioni: panoramica della piattaforma, tecniche
utili a migliorare siti gi esistenti, sviluppo di applicazioni lato
client. Un libro molto utile specialmente agli sviluppatori .NET
che vogliono approcciare le tecniche di progettazione tipiche
degli strumenti multi linguaggio in stile AJAX. Lo sforzo di
Esposito quello di mettere il lettore in gradi di realizzare
applicazioni AJAX interattive e veloci, capaci di regalare allu-
tente la sensazione di avere di fronte unapplicazione profes-
sionale che non risenta dei rallentamenti tipici delle applica-
zioni Web mal progettate. In questo orientamento, rivestono
particolare importanza la libreria Microsoft AJAX per il sup-
porto della programmazione JavaScript a oggetti e i controlli
ASP.NET AJAX Control Toolkit. Molto efficace anche la sezione
dedicata al rendering parziale della pagina, senza programma-
zione lato client: un buon utilizzo di questa tecnica consente,
infatti, la realizzazione di applicazioni particolarmente perfor-
manti e piacevoli da utilizzare.
Autore: Dino Esposito Casa Editrice: Mondadori Informatica
Collana: Miti Informatica Pagine: 336 ISBN: 88-6114-155-2
Prezzo: 14,50 Euro
CREARE SITI WEB CON JOOMLA! 1.5
U
n testo rivolto a sviluppatori Web,
designer e webmaster che voglia-
no realizzare un sito Web con un pro-
cesso il pi possibile semplice e lineare.
Joomla! garantisce una grande rapidit
nelle operazioni di sviluppo e deploy di
un sito ma, spesso, la sua estrema fles-
sibilit scoraggia gli sviluppatori dal-
landare a definire i numerosi parame-
tri offerti. Il libro d il meglio di s nel
rendere ancora pi rapido il processo
di creazione di un sito, stimolando al contempo la creativit degli
sviluppatori. Utile anche a chi ha scarse conoscenze di program-
mazione. Joomla! un sistema avanzato per la gestione di conte-
nuti. Viene utilizzato in tutto il mondo per tutto, dalle semplici
home page a complicati siti aziendali. facile da installare, facile
da gestire e molto affidabile, al punto da aver rubato grosse fette
di mercato a software a pagamento. Un solo numero per testimo-
niare il successo di questa piattaforma: oltre 20 milioni di instal-
lazioni a novembre 2007, con una crescita del 300% rispetto
allanno precedente. Se siete interessati alla installazione di
Joomla! questo testo rappresenta certamente un ottimo punto di
riferimento, sia per iniziare che per perfezionare i vostri siti sino
alla massima personalizzazione.
Autore: Hagen Graf Casa Editrice: McGraw Hill
Collana: Microcalcolatori Pagine: 310 ISBN: 9788838644979
Prezzo: 29,00 Euro
067:014-015 2-10-2008 11:12 Pagina 30
J CD J WEB
zend_tutorial_3.zip
cdrom.ioprogrammo.it
ht t p: / / www. i opr ogr ammo. i t
ioProgrammo Web
M
G
32
/Novembre 2008
La gestione degli account di un sito web
N
elle parti precedenti di questo tutorial
abbiamo visto come installare Zend
Framework e costruito una prima appli-
cazione che sfrutta il modello MVC per gestire
laccesso con autenticazione (tramite login e
password inserite in una tabella di un database
MySQL). Cos facendo, abbiamo usato diversi
componenti del framework, come quello che
permette agevolmente di gestire dei file di confi-
gurazione. In questa terza parte arricchiremo la
nostra applicazione di prova con alcune funzio-
ni avanzate. A differenza degli articoli passati, la
versione di riferimento di questo lultimissima,
la 1.6 RC2, per cui sar necessario aggiornare
linstallazione.
REQUISITI PER
LACCESSO AUTENTICATO
Una volta stabilita lintenzione di creare un
accesso con autenticazione, come visto nel pre-
cedente numero, dobbiamo poter gestire anche
la fase di richiesta degli account da parte degli
utenti. In particolare, dovremo costruire una
schermata in cui lutente dovr specificare le
credenziali (come si chiama e qual la sua e-
mail) e poi potr scegliere una login ed una pas-
sword di accesso (chiaramente non potremo
avere due login eguali). Il tutto dovr essere
gestito in modo tale che eventuali spider tentino
un accesso non autorizzato, per cui dovremo
aggiungere un CAPTCHA per difenderci da que-
sta eventualit.
In merito alle login ed alla password imporremo
che siano almeno di 6 caratteri e non oltre i 20.
Inoltre, ci interessa valutare che le-mail che
lutente inserisce non sia semplicemente for-
mattata come una e-mail, ma vogliamo avere la
ragionevole garanzia che il dominio a cui si rife-
risce esista realmente. Superati i vari controlli,
allutente verr presentata una schermata nella
quale lo si avvisa che, per attivare definitivamen-
te laccount, si dovranno seguire le istruzioni che
seguiranno per e-mail e che se non si convalida
laccount entro un dato numero di giorni (o di
ore) questo verr disattivato. Di fatto, tali istru-
zioni prevedono semplicemente un accesso ad
una URL specifica del nostro sistema per attiva-
re realmente laccount.
Chiaramente di tali operazioni ci interessa tene-
re un log; alcuni elementi vogliamo che vengano
registrati su file (magari XML), altri allinterno di
una tabella del nostro database di back end.
In base a quanto visto, la tabella della base dati
che gestisce lautenticazione deve essere estesa.
Deve infatti contenere, oltre i dati di username,
password e di stato dellaccount, anche le-mail,
il vero nome e cognome dellutente e la data di
creazione/modifica dellaccount. In particolare,
il campo status assume il valore -1 se laccount
stato inserito ma non ancora validato; un valore
da 0 a 4 in caso di account valido (dove il valore
indicher il numero di volte che si sbagliata la
password) e 5 in caso di account con troppi erro-
ri di accesso. Dato che stiamo considerando un
sistema LAMP, con un database esempio e,
come detto la volta scorsa, con un utente con
login/password zend/zend per accedervi, dovre-
Fig. 1: La schermata di accesso al sistema
che andremo a progettare
ZEND FRAMEWORK
TUTORIAL (Terza parte)
IN QUESTO NUOVO APPUNTAMENTO ESAMINEREMO IN DETTAGLIO COME ESTENDERE
LE NOSTRE WEB APPLICATION PER CONSENTIRE ALLE STESSE LA CREAZIONE E GESTIONE
DI ACCOUNT UTENTI. ALLUOPO FAREMO USO DEL PARADIGMA MVC
Conoscenze richieste
PHP - MYSQL
Software
PHP, APACHE, MYSQL
Impegno
Tempo di realizzazione
REQUISITI
032-037:032-035 30-09-2008 16:45 Pagina 32
ht t p: / / www. i opr ogr ammo. i t
mo eliminare la tabella users creata e rimpiaz-
zarla con una generata dal seguente script SQL:
CREATE TABLE `users` (
`USERNAME` varchar(20) NOT NULL,
`PASSWORD` varchar(20) NOT NULL,
`STATUS` int(10) DEFAULT '-1',
`EMAIL` varchar(80) NOT NULL,
`REALNAME` varchar(80) NOT NULL,
`DATA` datetime NOT NULL,
PRIMARY KEY (`USERNAME`)
) ENGINE=InnoDB
I COMPONENTI IN GIOCO
In base a quanto abbiamo visto la nostra
applicazione ha bisogno, oltre ai componenti
visti la volta scorsa, di:
1) ZEND_CAPTCHA, il componente per gesti-
re i CAPTCHA (usato in modo indiretto in
quanto inseriremo il CAPTCHA come un
campo del form di richiesta dati);
2) ZEND_EMAIL, il componente per gestire
linvo di e-mail allinterno del framework;
3) ZEND_LOG, il componente per gestire con
semplicit i log, siano essi registrati su file che
su tabella di database.
PAGINA DI CREAZIONE
ACCOUNT VIA MVC
Sfruttando sempre lapproccio MVC, per gesti-
re la nostra pagina di richiesta account andre-
mo a creare un nuovo controller che chiame-
remo Register che (come lIndex visto la volta
scorsa) gestir una form. Per accedere a que-
sta nuova pagina andremo ad aggiungere un
bottone che aprir la pagina in questione in
una nuova finestra, simile a quella standard di
una applicazione e non ad una di un browser.
Questo vuol dire che non deve avere elementi
come la barra dei menu e la sezione che indi-
ca la location. Dato che stiamo, di fatto, alte-
rando solo la parte grafica della schermata di
accesso, andremo ad agire sulla VIEW (vista)
della schermata di login (ovvero, seguendo
lalberatura standard che richiesta dal fra-
mework, andremo a toccare il file index.phtml
posto nella directory application\views\
scripts\index. In questo inseriremo il nuovo
bottone:
<center>
<input type="button" name="richiediaccount"
value="Richiedi un nuovo account"
onclick="openme('./index.php/register');" >
</center>
ed un minimo di codice Javascript per poter
aprire la finestra come di nostro gradimento (la
funzione openme richiamata dallevento onclick):
<script language="javascript">
function openme(url){
w = screen.width;
h = screen.height;
ah = h - 25;
nw=window.open(url,'','location=0,menubar=
0,tiblebar=0,toolbar=0,status=0,scrollbars=
1,resizable=1,width=' +w+',height='+ah);
window.nw.moveTo(0,0); }
</script>
CAPTCHA NELLA
NOSTRA APPLICAZIONE
Cominciamo con lesaminare ZEND_
CAPTCHA, un componente introdotto proprio
con la versione 1.6 del framework. Tramite
questo componente potremo infatti richiede-
re a un utente di inserire in un apposito
campo di un form il risultato di una ispezio-
ne visiva di qualcosa. In particolare, il com-
ponente mette a disposizione degli adapter
coi quali generare tale elemento comprensibi-
le solo per un utente reale. Questi adapter
sono:
1) DUMB questo il pi semplice e genera
una sequenza di lettere che devono essere
inserite nel campo invertendo lordine;
2) FIGLET questo permette di creare una
FIGlet di un dato numero di caratteri a video,
dove poi si chieder allutente di inserire tali
caratteri nel campo di testo ad hoc;
3) IMAGE questo il metodo pi gettonato
(ma previsto se e solo se il PHP stato com-
pilato con lestensione GD) e prevede la crea-
zione di una figura contenente una sequenza
di lettere particolarmente distorte per poter
rendere la vita dura ad un robot dotato di ICR;
4) RECAPTHCA questo metodo accede al
noto Web Service di gestione online dei CAPT-
CHA.
Nel nostro esempio, useremo questo ultimo
componente allinterno del form del Register
Control ler, che andremo ora ad esaminare.
M
ioProgrammo Web
Novembre 2008/
33
G
La gestione degli account di un sito web
Fig. 2: Una FIGLet generata da una schermata DOS
032-037:032-035 30-09-2008 16:45 Pagina 33
ht t p: / / www. i opr ogr ammo. i t
CREAZIONE
DELLA FORM DEL
REGISTERCONTROLLER
Il RegisterController avr una architettura interna
analoga a quella dellIndexController visto la volta
scorsa, ovvero la forma da esso gestita sar un
metodo della classe RegisterController. Mentre la
volta scorsa avevamo una semplice form con tre
campi, questa volta abbiamo un numero di
campi maggiore, ognuno dei quali ha diverse
regole di validazione aggiuntive oltre al semplice
essere obbligatorio. Pertanto la forma compatta
usata la volta scorsa diviene non pi cosi sempli-
ce, per cui andremo ad usare la forma estesa.
Il codice del form sar:
public function getRegisterForm()
{
// Form effettivo
$form = new Zend_Form;
$form->setMethod('post');
// Campo username
$username = $form->createElement('text',
'username');
$username->addValidator('alnum')
->addValidator('stringLength', false, array(6,
20))
->setRequired(true)
->addFilter('StringToLower')
->setLabel('Username');
// Primo campo password
$password_1 = $form-
>createElement('password', 'password_1');
$password_1->addValidator('StringLength',
false, array(6,20))
->setRequired(true)
->setLabel('Password');
// Secondo campo password, che deve essere
eguale al primo
$password_2 = $form-
>createElement('password', 'password_2');
$password_2->addValidator('StringLength',
false, array(6,20))
->setRequired(true)
->setLabel('Digitare nuovamente la Password');
// Campo della e-mail
$email = $form->createElement('text', 'email');
$email->addValidator($email-
>addValidator(new
Zend_Validate_EmailAddress(Zend_Validate_Hostnam
e::ALLOW_DNS)))
->setRequired(true)
->addFilter('StringToLower')
->setLabel('E-Mail');
// Campo CAPTCHA
$captcha = new Zend_Form_Element_Captcha
('controllo_captcha', array(
'label' => "Inserire il testo qui visibile nel
campo sottostante",
'captcha' => 'Figlet',
'captchaOptions' => array(
'captcha' => 'Figlet',
'wordLen' => 7,
'timeout' => 300,
),
));
// Campo realname
$realname = $form->createElement('text',
'realname');
$realname->setRequired(true)
->setLabel('Nome e Cognome reali');
$form->addElement($username)
->addElement($password_1)
->addElement($password_2)
->addElement($email)
->addElement($captcha)
->addElement($realname)
->addElement('submit', 'login', array('label' =>
'Inserire le credenziali di accesso'));
return $form;
}
Come si pu notare, abbiamo diversi elementi di
validazione. In particolare per username e pas-
sword richiesto che siano (come da specifica)
comprese tra i 6 ed i 20 caratteri, mentre per le-
mail vi un validatore ad hoc:
$email->addValidator( $email->addValidator( new
Zend_Validate_EmailAddress(Zend_Validate_
Hostname::ALLOW_DNS)))
Cos, il campo e-mail verr valutato solo per la
sua forma (per cui pippo@pluto.com sar conside-
rate una e-mail valida, mentre pippo@pluto non lo
sar). Se vogliamo aggiungere una validazione
anche del dominio (ovvero vogliamo verificare
che pluto.com pu ricevere delle e-mail, allora
dovremo abilitare esplicitamente tale feature
(presente solo se la nostra applicazione risiede
su un server UNIX) col seguente costrutto:
$email->addValidator( $email->addValidator( new
Zend_Validate_EmailAddress(Zend_Validate_
Hostname::ALLOW_DNS, true)))
Altro elemento di interesse il CAPTCHA. Come
si vede la creazione veramente banale. Si noti
come nel caso si possa (o voglia) usare una
immagine, questa verr costruita e fisicamente
archiviata sul file system della nostra macchina
in una sottodirectory della directory public (per
la precisione sar archiviata in public\ima -
ges\captcha, per cui dovremo per prima cosa assi-
curarci di creare tale directory); inoltre dovremo
specificare anche il font con cui saranno creati i
ioProgrammo Web
M
G
34
/Novembre 2008
La gestione degli account di un sito web
NOTA
MVC
Il paradigma MVC un
pattern architetturale
dellingegneria informatica
che prevede la
separazione della business
logic dallinterfaccia. Tale
separazione permette di
modificare la parte di
logica senza toccare
quella visuale (e viceversa)
per cui nel complesso
lapplicazione pi
facilmente mantenibile e
gestibile.
032-037:032-035 30-09-2008 16:45 Pagina 34
ht t p: / / www. i opr ogr ammo. i t
caratteri (con lapposito metodo font).
GESTIONE
DELLA FORM DEL
REGISTERCONTROLLER
Come detto, la IndexAction del controller in que-
stione avr una struttura molto semplice.
Dovremo infatti discernere se ci troviamo o
meno a valle di un Post del form. In caso afferma-
tivo, dopo aver valutato la correttezza dei valida-
tori impostati prima, potremo andare a fare delle
elaborazioni aggiuntive, altrimenti andremo a
presentare il form allutente (dove nel caso di
validazione errata verranno aggiunti automati-
camente dei congrui messaggi di errore):
public function indexAction()
{
$form = $this->getRegisterForm();
if ($this->getRequest()->isPost())
{
// Accesso a seguito di submit
if ($form->isValid($_POST))
{
// Form valido dal punto di vista
dei validatori standard

} else {
// Form non valido, viene
ripresentato all'utente
echo "ERRORE di VALIDAZIONE
dei campi - controllare quanto inserito";
$this->view->form = $form;
}
} else {
// Accesso iniziale, visualizzazione del form
$this->view->form = $form;
}
}
I test che dovremo aggiungere sono:
1) eguaglianza delle due password nei due
campi;
2) presenza di uno username come quello scelto.
Se il primo si ottiene semplicemente con la strut-
tura:
if ( $form->getValue('password_1') == $form-
>getValue('password_2') )
{
.// Codice successivo
}
else
{
// Form non valido, viene ripresentato all'utente
echo "ERRORE le due password inserite non sono
eguali - controllare";
$this->view->form = $form;
}
Per il secondo dovremo invece accedere alla
tabella utenti sfruttando, come visto la volta scor-
sa, il database adapter messo a disposizione dal
framework, assieme ai dati di accesso al DB con-
servati in un file XML:
// Caricamento dati di configurazione
$config = new Zend_Config_Xml('./config.xml',
'database');
$db_host = $config->db_host;
$username = $config->username;
$password = $config->password;
$db_name = $config->dbname;
// Inizializzazione del dbAdapter
$connection_string = array('dbname' => $db_name,
'username' => $username , 'password' =>
$password , 'host' => $db_host);
$dbAdapter = new
Zend_Db_Adapter_Mysqli($connection_string);
// Verifica che lo username scelto non sia gi
esistente
$sql = "select count(*) as rst from users where
username = '" . $form->getValue('username') . "'";
$res = $dbAdapter->fetchAssoc($sql);
$num = $res[1]['rst'];
if ( $num == 0 )
{
. // Codice successivo
}
else
{
M
ioProgrammo Web
Novembre 2008/
35
G
La gestione degli account di un sito web
NOTA
CAPTCHA
Lacronimo CAPTCHA sta
per: Completely
Automated Turing test to
tell Computers and
Humans Apart (Test
automatizzato di Turing
per poter discernere
computer da umani),
ovvero un test che
concepito per poter essere
superato solo e soltanto da
un utente reale e non da
un computer.
ovviamente usato
proprio per evitare, come
nel nostro caso, la
creazione di account
massivi da parte di robot.
Lacronimo stato
concepito nel 2000 da Luis
von Ahn, Manuel Blum,
Nicholas J. Hopper
(Carnegie Mellon
University), e John
Langford (IBM).
Fig. 3: Lalberatura della nostra applicazione MVC
032-037:032-035 30-09-2008 16:45 Pagina 35
ht t p: / / www. i opr ogr ammo. i t
// Errore: username gi previsto nel sistema
echo "ERRORE: nel sistema esiste gia' uno
username come quello indicato - controllare";
$this->view->form = $form;
}
Si noti come il risultato della query viene inserito
in una variabile ($res) che strutturata come un
array associativo grazie al comando fetchAssoc.
A questo punto non ci rimane che effettuare i
passi di inserimento dellutente nella base dati e
di invio delle-mail allo stesso. Il primo molto
semplice, avendo la connessione gi aperta col
DB:
$sql = "insert into users
(USERNAME,PASSWORD,EMAIL,REALNAME,DATA)
values ('" . $form->getValue('username') . "','" .
$form->getValue('password_1') . "','" . $form-
>getValue('email') . "','" . $form-
>getValue('realname') . "',NOW())";
$dbAdapter->query($sql);
Si noti come si usata la funzione NOW() di
MySQL per inserire il campo della data.
Per inviare le-mail, invece, dovremo usare il
componente ZEND_MAIL. Questo permette sia di
inviare e-mail verso un server, sia di leggerle da
un server POP3. Nel caso di invio, tale operazio-
ne verr effettuata di default con lausilio di
SENDMAIL; tuttavia, qualora nella nostra rete
avessimo un mail server ben identificato che for-
nisse servizi SMTP, allora verrebbe pi comodo
usare questo per inviare la posta. Se questo ser-
ver risponde a out.miodominio.it, il codice che
ci permette di impostare questo come il metodo
di invio il seguente:
$tr = new
Zend_Mail_Transport_Smtp('out.miodominio.it');
Zend_Mail::setDefaultTransport($tr);
Fatto ci, potremo finalmente esaminare il codi-
ce che permette linvio effettivo:
$testo_email = Testo e-mail
$mail = new Zend_Mail();
$mail->setBodyText($testo_email);
$mail->setFrom('admin@miodominio.it ', 'Servizio
XXXX');
$mail->addTo($form->getValue('email'));
$mail->setSubject('Creazione account al servizio
XXXX');
$mail->send();
In questo modo viene inviata una e-mail testua-
le allindirizzo che lutente ha prima inserito. Per
cui, in base ad i nostri requisiti, il testo che
vogliamo inviare allutente deve contenere una
URL completa, dove andare ad attivare definiti-
vamente laccount. Dato che sicuramente pi
comodo fare in modo che lutente possa accede-
re a tale URL seguendo un hyperlink, allora pos-
siamo usare una e-mail in formato HTML, che
oramai viene gestito da praticamente tutti i mail
client. In tal caso il metodo da usare
setBodyHtml invece di setBodyText. Chiaramente
la variabile $testo_email potr contenere un
testo HTML.
Si noti come anche possibile, in modo sempli-
ce ed efficiente, inviare degli attachment alla e-
mail. Ad esempio: supponiamo di aver scelto un
CAPTCHA di tipo immagine e di volerla inviare
come attachment (perch si vuole che lutente la
reinserisca nella fase di attivazione, come ulte-
riore sicurezza). Se limmagine generata pub-
lic\images\captcha\immagine.gif, allora il codice
che ci permette linvio della stessa (da inserire
prima del punto di send visto sopra) sar:
$at = $mail->createAttachment($myImage);
$at->type = 'image/gif';
$at->disposition =
Zend_Mime::DISPOSITION_INLINE;
$at->encoding = Zend_Mime::ENCODING_8BIT;
$at->filename =
'public\images\captcha\immagine.gif';
Inviata le-mail, potremo ridirezionare la pagina
ad una schermata di conferma dellavvenuta
registrazione (pagina non riportata per brevit).
$this->_redirect('http://127.0.0.1/
quickstart/public/conferma.php');
INSERIMENTO
DEGLI ELEMENTI
DI LOG: ZEND_LOG
Linserimento di elementi di tracciamento (log)
allinterno delle nostre applicazioni una pratica
comune e ben nota. In genere si vuole che tali
tracciamenti, oltre a riportare la data ed ora,
assieme ad un messaggio, siano corredati di un
elemento che ne permette la classificazione.
In questo modo potremo distinguere eventi di
diverso tipo, come ad esempio tracciamenti di
errori applicativi gravi da quelli meno gravi.
Chiaramente tali informazioni verranno in gene-
re salvate in file (spesso in formato XML) o in un
database. Compito del componente ZEND_LOG
rendere quanto visto una operazione banale.
In particolare, questo componente di base preve-
de un log contenente 4 informazioni: un time-
stamp, un livello di priorit (o di classifica)
ioProgrammo Web
M
G
36
/Novembre 2008
La gestione degli account di un sito web
NOTA
FRANK, IAN AND
GLENNS LETTERS
FIGlet un acronimo che
sta per Frank, Ian and
Glenn's LETters, che sono
i tre autori dellomonimo
programma che permette
di creare delle lettere di
una certa dimensione
composte da caratteri
ordinari. Un esempio della
versione DOS riportato in
Fig. 2. In genere tale
tecnica si usa nelle e-mail
a caratteri come una sorta
di signature. I sorgenti del
programma sono
liberamente scaricabili da
http://www.figlet.org
032-037:032-035 30-09-2008 16:45 Pagina 36
ht t p: / / www. i opr ogr ammo. i t
numerico che va da 0 a 7 (0 priorit maggiore),
un livello di priorit testuale ed un messaggio.
Chiaramente il timestamp autogenerato e i
livelli numerico e testuale devono (per loro natu-
ra) essere allineati (per inciso, Zend usa la classi-
ficazione standard BSD del protocollo syslog).
Per cui lutente andando a scrivere un log dovr
essenzialmente scegliere il livello di priorit e
indicare il messaggio. Queste due informazioni
verranno passate a un writer, che si occuper di
scriverle dove si deciso.
Supponiamo, ad esempio, di voler tenere un log
del fatto che un utente ha correttamente richie-
sto un nuovo account e di voler conservare tale
informazione in una tabella del database (che
chiameremo logger).
La struttura di tale tabella, in base a quanto visto,
sar:
CREATE TABLE `logger`
(
`timestamp` varchar(100) NOT NULL,
`priorityName` varchar(20) NOT NULL,
`priority` int(11) NOT NULL,
`message` varchar(200) NOT NULL
)
ENGINE=InnoDB
Il codice che ci permette di definire il writer su
questa tabella sar il seguente:
$db = Zend_Db::factory('PDO_MYSQL',
$connection_string);
$columnMapping = array('priority' => 'priority',
'message' => 'message', 'priorityName' =>
'priorityName', 'timestamp' => 'timestamp');
$writer = new Zend_Log_Writer_Db($db, 'logger',
$columnMapping);
$logger = new Zend_Log($writer);
In cui $connection_string linsieme dei parame-
tri di connessione visti nel codice precedente.
Si noti come sia stato necessario indicare un
mapping tra gli elementi che compongono il log
creato da Zend e le colonne della base dati (che
per semplicit sono state chiamate in modo
eguale).
A questo punto, per inserire un messaggio di log
(a prescindere dal writer effettivo) useremo il
metodo log, che prende in ingresso il messaggio
e la priorit:
$logger->log('Messaggio di tipo
INFO',Zend_Log::INFO);
Infine, per chiudere il log sar sufficiente usare il
comando:
$logger = null;
In caso invece si voglia salvare i dati su un file
XML dovremo usare un writer di tipo stream di
dati verso un file ed indicargli che vogliamo un
formato XML (altrimenti sar plain text).
Anche in questo caso possiamo indicare, con un
array associativo, un mapping tra gli elementi del
log ed i tag XML tra cui vanno racchiusi in modo
analogo a come si aveva il mapping con le colon-
ne della tabella visto prima.
$writer = new
Zend_Log_Writer_Stream('/path/to/logfile');
$formatter = new Zend_Log_Formatter_Xml('log',
$columnMapping);
$writer->setFormatter($formatter);
CONCLUSIONI
In questo terzo articolo abbiamo visto come una
serie di funzioni standard di una Web
Application, come la gestione dei CAPTCHA,
linvio di messaggi di posta elettronica ad un
utente e la gestione di LOG vengano enorme-
mente semplificate usando il Zend Framwork,
che permette di usarle inserendo poche righe di
codice. Col prossimo articolo espanderemo
ancora di pi la nostra applicazione con ulteriori
elementi del framework.
Guido Pennella
M
ioProgrammo Web
Novembre 2008/
37
G
La gestione degli account di un sito web
LAUTORE
Guido Pennella un
ingegnere esperto di
informatica, laureato
allUniversit di Roma La
Sapienza. Si occupa
principalmente dello
sviluppo di sistemi sia via
Web, sia di applicazioni
embedded real time
distribuite. possibile
contattarlo allindirizzo di
posta elettronica
guidopennella@virgilio.it
Fig. 4: Le priorit definite dal protocollo BSD
syslog
Per quanto non necessario, comunque
consigliabile usare un ambiente di
sviluppo integrato per poter gestire
agevolmente lo sviluppo di Web
Applications in PHP cosa in generale
sempre vera per la programmazione di
sistemi. La stessa Zend, in
collaborazione con la IBM, ha rilasciato
Open Source anche un ambiente di
sviluppo: il PDT PHP Development
Tools. Questo un insieme completo di
componenti per Eclipse per sviluppare
applicazioni in PHP. Il progetto ben
vivo (il 10 giugno 2008 stata rilasciata
la versione 1.0.3) e la sua home page :
http://www.eclipse.org/pdt/.
Per i neofiti di Eclipse, si consiglia luso
del pacchetto all-in-one, installabile
semplicemente decomprimendolo in una
directory.
PHP DEVELOPMENT TOOLS
032-037:032-035 30-09-2008 16:45 Pagina 37
LA FASE DI BUILDING
DI UN PROGETTO
QUESTA RICETTA SPIEGA COME ESPORTARE IL LAVORO SVOLTO IN UN PROGETTO JAVA
DI ECLIPSE, IN MODO DA POTER CONSEGNARE A TERZI GLI ESEGUIBILI PRODOTTI,
LA DOCUMENTAZIONE ED, EVENTUALMENTE, I SORGENTI DEL PROPRIO SOFTWARE
A
nulla servirebbe un ambiente tanto ricco e
completo come Eclipse se, ad un certo
punto, non fosse possibile esportare i risul-
tati raggiunti. Bench nessun software possa mai
definirsi perfetto, ma sempre e solo perfezionabile,
arriva sempre un momento in cui si decide di realiz-
zare una build e di renderla disponibile agli altri.
necessario allora impacchettare gli eseguibili e
generare la documentazione. Nel caso di un softwa-
re Open Source, bisogna anche estrarre i sorgenti.
La presente ricetta spiega come compiere queste tre
semplici operazioni.
UN PROGETTO
ESEMPLIFICATIVO
Creiamo un progetto Java chiamato MiaLibreria,
come insegnato nella ricetta #2 (ioProgrammo 130).
Immagineremo di realizzare una libreria che, a lavo-
ro concluso, sar resa disponibile ad altri sviluppa-
tori, affinch la utilizzino per i loro software. Nella
cartella dei sorgenti creiamo i pacchetti package1 e
package2. Dentro ciascuno dei pacchetti inseriamo
un documento HTML chiamato package.html con
della documentazione circa i contenuti del pacchet-
to stesso. Realizziamo ora quattro classi, disposte
due per pacchetto, e chiamiamole Classe1, Classe2,
Classe3 e Classe4. Allinterno di ciascuna classe inse-
riamo alcuni metodi a piacere, e decoriamo il codi-
ce con dei commenti javadoc. Nel pacchetto con il
codice allegato alla ricetta potete trovare una strut-
tura di questo tipo gi pronta.
ESPORTARE UN JAR
Gli eseguibili Java, come dovrebbe essere gi noto e
come ribadito nella ricetta #3 (ioProgrammo 131),
vengono raccolti allinterno di archivi JAR, speciali
file con estensione .jar, che altro non sono che dei
pacchetti ZIP organizzati secondo una certa struttu-
ra. Questi archivi contengono i file .class che costi-
tuiscono il compilato di unapplicazione o di una
libreria, pi eventuali risorse ad essi collegate.
Selezioniamo la radice del progetto e scegliamo, dal
men, la voce File Export. Questa operazione
comporta lapertura di una finestra che permetter
la scelta fra le differenti procedure di Export dispo-
nibili. Per esportare un archivio JAR, come nostra
intenzione fare, bisogna selezionare la voce JAR
File, nel gruppo Java, e poi procedere con il tasto
Next.
A questo punto il wizard per lesportazione del JAR
avviato. La prima tra le sue schermate ci permette di
scegliere, nella parte alta, quali risorse includere nel
JAR che sar creato.
Scegliamo cosa includere nel nostro JAR, utilizzando
gli strumenti presenti nella met alta della scherma-
ta. Non necessario includere i file package.html,
che fanno parte della documentazione, pertanto
andiamo a deselezionarli. Facciamo in modo che
restino selezionate soltanto le quattro classi presen-
ti nei due pacchetti del progetto. Le quattro caselle
di spunta immediatamente sotto gli elenchi di sele-
zione permettono di affinare ulteriormente la scelta.
Normalmente si seleziona soltanto la voce Export
generated class files and resources. Questa fa in
modo che, di ciascuna classe selezionata, venga
inclusa nel pacchetto la versione compilata (i file
.class generati dal compilatore, in pratica).
SOFTWARE M Esportare eseguibili, documentazione e sorgenti
ht t p: / / www. i opr ogr ammo. i t
G
38
/Novembre 2008
J CD J WEB
eclipse-export.zip
cdrom.ioprogrammo.it
Conoscenze richieste
Rudimenti di Java
Software
Java SDK (JDK), Eclipse
Impegno

Tempo di realizzazione
REQUISITI
Fig. 1: La struttura iniziale del progetto MiaLibreria
038-041:088-093-corsi-xsl 30-09-2008 16:31 Pagina 38
ht t p: / / www. i opr ogr ammo. i t
Novembre 2008/
39
G
Esportare eseguibili, documentazione e sorgenti M
SOFTWARE
Opzionalmente possibile anche includere i sor-
genti .java nel JAR stesso, attivando la voce Export
Java source files and resources, ma generalmente
questa pratica non molto utile. Gli archivi JAR sono
infatti concepiti per contenere gli eseguibili, non il
codice. Impareremo oggi stesso una tecnica pi pro-
ficua per lexport dei sorgenti.
Nella met inferiore della schermata si seleziona la
destinazione su disco del file JAR che sar generato
ed alcune opzioni generali, come ad esempio se si
intende comprimere o meno il contenuto dellarchi-
vio (personalmente vi suggerisco di s).
Una volta compiute le scelte desiderate possibile
procedere alla schermata successiva, con il solito
tasto Next.
La seconda schermata propone alcune altre scelte:
includere o meno quelle classi che possono com-
portare alcuni problemi, o se sia necessario compi-
lare il progetto qualora questo non fosse compilato
automaticamente. Sono tutte caratteristiche che
imparerete ad utilizzare man mano che crescer la
vostra confidenza con la piattaforma. Interessante
lultima del gruppo, cio quella etichettata Save the
description of this JAR in the workspace. Attivando
questa opzione, ad export ultimato, sar inserito un
file allinterno del workspace di Eclipse con esten-
sione .jardesc. Questo file utile per ripetere in qual-
siasi momento lexport del JAR senza dover riattra-
versare tutto il wizard. Pertanto vi consiglio di attiva-
re lopzione e di scegliere di conservare il file genera-
to allinterno del progetto stesso, magari con nome
export-jar.jardesc.
Passiamo ora alla terza ed ultima schermata del
wizard.
Qui possibile compiere le ultime scelte disponi-
bili prima della generazione. Anzitutto possibi-
le manipolare il file MANIFEST che sar introdot-
to nel JAR. Eclipse ne genera uno automatica-
mente che riflette le scelte fatte nel wizard ma, se
volete, potete anche usarne uno realizzato da
voi. Se lasciate fare ad Eclipse, come avviene
nella maggior parte dei casi, potete inoltre farvi
salvare una copia del file generato nel workspa-
ce.
Le ultime due opzioni disponibili permettono di
sigillare i pacchetti (cfr. documentazione Java) e
di specificare una classe di avvio del pacchetto.
Questultima opzione risulta molto utile se si sta
esportando un software per lutente finale, piut-
tosto che una libreria come nel nostro caso. Un
software per lutente, infatti, dispone sicuramen-
te di un punto di avvio, cio di una classe con un
metodo main(). In questo caso possibile sele-
zionare la classe di questo tipi inclusa nel JAR da
generare. In questo modo lapplicazione potr
essere facilmente avviata (nei sistemi Windows,
ad esempio, un doppio clic sui JAR di questo tipo
sufficiente per attivare la macchina virtuale e
lanciare il programma).
Eseguita ogni scelta non resta che utilizzare il tasto
Finish e attendere la creazione del file .jar, che
potrete poi ritirare direttamente dal disco rigido,
alla posizione specificata nella prima schermata del
wizard. Un effetto collaterale della creazione del JAR,
se avete seguito il mio consiglio di attivare la voce
Save the description of this JAR in the workspace,
che il vostro progetto conterr ora un file export-
jar.jardesc.
Cliccando con il tasto destro sul file .jardesc
generato possibile scegliere la voce Create
Fig. 2: La prima schermata del wizard di esportazione
di un archivio JAR
NOTA
RUNNABLE JAR
FILE
A partire da Eclipse 3.4
stato messo a disposizione
il nuovo wizard di
esportazione chiamato
Runnable JAR file. Come
il nome lascia ad intendere,
questa procedura pu
essere utilizzata per
esportare pi velocemente
quegli archivi JAR al cui
interno contenuta e
registrata una classe
dotata di metodo main(),
cio il punto di lancio
dellapplicazione..
Fig. 3: Il file generato dal wizard, che permette di
ricreare il JAR pi volte senza dover ripercorrere
lintero wizard
038-041:088-093-corsi-xsl 30-09-2008 16:31 Pagina 39
SOFTWARE M Esportare eseguibili, documentazione e sorgenti
ht t p: / / www. i opr ogr ammo. i t
G
40
/Novembre 2008
JAR, per ricreare nuovamente lo stesso JAR,
oppure Open JAR packager, per modificare
alcune delle scelte fatte prima di procedere alla
nuova creazione.
ESPORTARE I JAVADOC
Se con Eclipse creare un archivio JAR semplice,
esportare la documentazione javadoc di un proget-
to, lo ancora di pi. Un requisito da soddisfare
affinch la documentazione sia veramente utile ,
naturalmente, quello di aver scritto del codice con
commenti javadoc completi e ben fatti. Una volta
ultimato questo lavoro, possibile generare le pagi-
ne HTML con la documentazione di pacchetti, clas-
si e metodi. La cosa, di per s, molto utile soprat-
tutto nel caso in cui si stia producendo una libreria
che altri sviluppatori utilizzeranno per programma-
re. Selezionate i pacchetti di cui desiderate generare
la documentazione javadoc, o lintera cartella src se
volete documentazione per tutte le classi del proget-
to, quindi dal men scegliete di nuovo la voce File
Export.
Questa volta, nella schermata di selezione del
wizard di export, scegliete la voce Javadoc, sempre
nel gruppo Java.
Il primo passo del wizard ricorda molto
lesportazione del JAR. Anche in questo caso si sele-
ziona tra i pacchetti e le classi del progetto, in modo
da scegliere in maniera granulare cosa far parte
della documentazione e cosa ne rester fuori. pos-
sibile (in alto) scegliere quale eseguibile sar richia-
mato per la generazione dei javadoc (per default
viene utilizzato quello compreso nel JDK predefinito
del workspace, ma la scelta pu essere arbitraria-
mente modificata). Quindi possibile scegliere il
livello di visibilit. La voce private produce la docu-
mentazione pi dettagliata, includendo anche la
descrizione dei metodi privati. Le voci successive
sono a scendere, fino a public che produce docu-
mentazione per metodi e propriet pubblici.
Normalmente, quando si produce documentazione
per una libreria, si sceglie protected, che documenta
i membri pubblici e quelli protetti. Lultima parte
della schermata permette di scegliere se utilizzare la
doclet predefinita di javadoc o una customizzata (la
doclet la classe che genera la documentazione, che
possibile alterare per avere documentazioni pi
personalizzate). Nel caso di utilizzo della doclet
standard, che poi quello pi ricorrente, bisogna
specificare quale la cartella che ospiter la docu-
mentazione generata. Tale directory pu essere
allinterno del progetto e del workspace, come sug-
gerisce per scelta predefinita Eclipse, oppure dove
volete sul vostro file system. Normalmente bene
inserire una cartella doc allinterno del progetto stes-
Fig. 5: La prima schermata del wizard di esportazione
della documentazione javadoc
Fig. 6: La seconda schermata del wizard di esportazio-
ne della documentazione javadoc
Fig. 4: Per avviare il wizard di esportazione della docu-
mentazione javadoc bisogna selezionare Java
Javadoc
NOTA
SCORCIATOIE UTILI
Le seguenti scorciatoie
sono valide durante
lutilizzo delleditor Java:
G CTRL + /
Disattiva la riga corrente o
il blocco di codice
selezionato, trasformandolo
in un commento. Ripetendo
nuovamente loperazione
sulla medesima riga o sullo
stesso blocco, il commento
viene rimosso e tutto torna
come era prima.
G F2
Mostra, in una finestra
pop-up, leventuale
documentazione javadoc
associata allelemento sul
quale posizionato il
cursore del testo.
G SHIFT + F2
Apre nel browser
predefinito del sistema
leventuale
documentazione javadoc
associata allelemento sul
quale posizionato il
cursore del testo.
G F3
Apre il sorgente
dellelemento sul quale
posizionate il cursore del
testo.
038-041:088-093-corsi-xsl 30-09-2008 16:31 Pagina 40
ht t p: / / www. i opr ogr ammo. i t
so, proprio come suggerisce Eclipse per default.
Compiute le scelte necessarie possibile muoversi
alla seconda schermata del wizard, con il tasto
Next.
In questa schermata, per cominciare, possibile
dare un titolo personalizzato alla documentazione.
Generalmente si mette il nome del software o della
libreria, seguito dal numero di versione (ad esempio
MiaLibreria 1.1). Tutte le opzioni sottostanti per-
mettono un controllo molto granulare su cosa
entrer a far parte dei javadoc generati: possibile
decidere se includere o meno un indice e delle pagi-
ne di navigazione correlate, se riportare o meno le
eventuali informazioni presenti nel codice circa
lautore e la versione di ciascuna classe, ed altro
ancora. La parte nella met centrale permette di
creare link verso documentazioni javadoc pre-esi-
stenti, solitamente disponibili online. Lultima delle
opzioni della schermata permette di modificare, se
ne sentite proprio lesigenza, laspetto delle pagine
HTML che saranno generate, attraverso luso di un
foglio di stile personalizzato. Ancora Next per pas-
sare alla terza e ultima schermata del wizard.
Prima di procedere alla generazione sono possibili
alcune altre scelte: linserimento di un file HTML di
overview (sar utilizzato nella pagina principale
della documentazione, per introdurre lintera libre-
ria o applicazione), opzioni per la macchina virtuale
che eseguir il comando javadoc e la doclet, impo-
stazioni per la compatibilit dei sorgenti ed altro
ancora. Da segnalare la possibilit di generare uno
script di Ant per ripetere successivamente la genera-
zione della documentazione javadoc. Si tratta delle-
quivalente del file .jardesc nel caso di esportazione
dei JAR, anche se la funzionalit meno immediata
perch richiede lintegrazione con Ant (di Ant si par-
ler sicuramente in una ricetta futura). Lultima delle
opzioni permette di aprire la documentazione con il
browser, non appena sar stata generata. Cliccando
su Finish, al primo atto di generazione, vi verr
probabilmente chiesto se volete adottare la cartella
scelta nella prima schermata come la predefinita per
la documentazione del progetto aperto. Fate pure la
vostra scelta.
Dopo aver generato la documentazione, dovreste
ritrovare una cartella doc allinterno del progetto (se
non avete scelto una locazione differente). Il file
index.html rappresenta il punto di partenza per
lesplorazione dei documenti. Potete trasferire la
documentazione fuori dal workspace di Eclipse con
una semplice operazione di copia-incolla della car-
tella doc, dal progetto allesplora risorse o analogo
del vostro sistema operativo.
ESPORTARE I SORGENTI
Esportare i sorgenti la pi semplice delle tre opera-
zioni del giorno: selezionate la cartella src, o un suo
sottoinsieme se non desiderate esportarla comple-
tamente, quindi adoperate unoperazione di copia-
incolla tra il Package Explorer di Eclipse e il file
manager del sistema operativo.
In alternativa potete provare qualcuno degli altri
wizard di export inclusi in Eclipse, ad esempio
Archive File o File System sotto la voce General.
NELLA
PROSSIMA RICETTA
La ricetta del prossimo mese vi insegner come si
utilizza il debugger, strumento indispensabile per il
bugfix di qualsivoglia software. Se non avete mai uti-
lizzato un debugger prima, e in particolare quello di
Eclipse, non potete assolutamente perdere la prossi-
ma ricetta: apprenderete lutilizzo di uno strumento
in grado di dimezzare il tempo necessario per la
risoluzione di qualsiasi problema presente nel
vostro codice.
Carlo Pelliccia
Esportare eseguibili, documentazione e sorgenti M
SOFTWARE
Novembre 2008/
41
G
Fig. 7: La terza (ed ultima) schermata del wizard di
esportazione della documentazione javadoc.
Fig. 8: Screenshot della documentazione generata
038-041:088-093-corsi-xsl 30-09-2008 16:31 Pagina 41
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
42
/Novembre 2008
Tecniche Ruby per la risoluzione di problemi complessi
L
a deformazione professionale pu influire an-
che sul modo in cui affrontiamo un passa-
tempo come il Sudoku. Da programmatori la
propensione naturale quella di riflettere sulle logi-
che che adottiamo per risolverli e modelizzarle.
In questo modo affrontiamo una sfida diversa: non ri-
solvere semplicemente uno schema ma capire qua-
li siano le tecniche adatte a risolverli tutti e poter con-
frontare fra loro queste tecniche.
La risoluzione di un Sudoku molto spesso richiede
l'alternarsi fra operazioni meccaniche, come il con-
trollare per ogni riga in quale casella si possa piaz-
zare un certo valore, e situazioni in cui i metodi pi
semplici e ripetitivi non bastano e dobbiamo trova-
re soluzioni pi creative e complesse. Il problema
che molto spesso le fasi cha abbiamo definito mec-
caniche richiedono molto tempo e non sono molto
stimolanti. L'idea , innanzitutto, quella di delegare
l'applicazione delle regole pi banali (ma lunghe e
tediose) a qualcosa di molto pi preciso e paziente
di noi: un programma scritto in Ruby, un vero e pro-
prio risolutore logicodi Sudoku. La sfida sar far cre-
scere il nostro risolutore ogni volta che incontrere-
mo uno schema che non sia in grado di risolvere, in-
segnandogli una nuova euristica, un nuovo modo di
procedere. Potremo anche utilizzare il nostro risolu-
tore per compiere solo i passi pi banali ed eseguire
personalmente i passaggi pi complessi di modo da
concentrarci solo sulla parte davvero interessante
della risoluzione.
Inizieremo vedendo come rappresentare i diversi
concetti presenti nel gioco; poste le basi implemen-
teremo le prime regole di risoluzione per poi esami-
narne una pi complessa e concluderemo lascian-
do aperto qualche spunto per continuare a migliorare
il nostro risolutore.
RAPPRESENTAZIONE
DELLO SCHEMA
Per prima cosa realizzeremo una classe le cui istan-
ze rappresenteranno uno schema di gioco. Ovvia-
mente, ogni istanza dovr memorizzare lo stato del-
le caselle: se contengono un valore oppure no e se si
di quale valore si tratta. Per il nostro scopo andr be-
nissimo un array di array (variabile @values) che con-
tenga, in corrispondenza di ogni casella, il valore del-
la medesima, oppure 0 per indicare che la casella vuo-
ta.
Avremo poi bisogno di un'altra informazione da as-
sociare a ogni casella: una lista di valori che abbia-
mo scoperto compatibile con la casella. In questo
caso optiamo per una tabella di hash (variabile @ex-
clusions) che utilizzer come chiave un array con le
coordinate della casella. In questo modo creeremo
le liste di valori esclusi solo quando ci serviranno.
Qui di seguito i metodi per leggere i dati che abbiamo
appena definito:
def value(row,col)
@values[row][col]
end
def exclusions(row,col)
r = @exclusions[[row,col]]
if !r
r = @exclusions[[row,col]] = []
end
r
end
come vedete, nel caso in cui sia richiesta una lista dei
valori esclusi per una casella, questa sar creata al
momento e salvata prima di restituirla.
Esaminiamo i metodi per la modifica di questi valo-
ri:
# Se il valore era gi assegnato viene restituito false,
# se si tratta in effetti di un nuovo assegnamento viene
# restituito true. Se si tratta di un tentativo di cambiare
# un valore assegnato viene sollevata un'eccezione
def set_value(row,col,v)
old_v = @values[row][col]
if old_v != 0
if old_v == v
return false
else
raise "tentativo di assegnare nuovo valore
UN RISOLUTORE
LOGICO DI SUDOKU
SCOPO DI QUESTO ARTICOLO IMPLEMENTARE UNAPPLICAZIONE CHE RISOLVE IL GIOCO
DEL SUDOKU. PER FARE QUESTO ADOPEREREMO RUBY, IMPLEMENTANDO METODI
EURISTICI E AVVANTAGGIANDOCI DELLE STRUTTURE PROPRIE DEL LINGUAGGIO
J CD J WEB
sudoku_solver.rb
cdrom.ioprogrammo.it
Conoscenze richieste
Conoscenza del
linguaggio Ruby di base
e uso delle closure
Software
Interprete Ruby
Impegno

Tempo di realizzazione
REQUISITI
042-046:072-080 30-09-2008 16:05 Pagina 42
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
43
G
Tecniche Ruby per la risoluzione di problemi complessi
differente alla casella [#{row},#{col}]"
end
else
@values[row][col] = v
true
end end
def exclude(row,col,v)
ex = exclusions(row,col)
if ex.include?(v)
false
else
ex.push(v)
true
end end
Questi metodi sono leggermente pi complessi per-
ch dobbiamo, non soltanto registrare il valore, ma an-
che verificare che si tratti di un valore novit, quin-
di non ancora assegnato n escluso in precedenza.
Restituiremo il valore true solo per gli effettivi cam-
biamenti; questo ci torner utile per capire, a ogni
iterazione, se i nostri algoritmi hanno realmente in-
dividuato dei valori utili alla risoluzione.
CARICAMENTO
DEGLI SCHEMI
Risulter molto utile poter caricare gli schemi da fi-
le. Il formato di questi file sar il pi semplice possi-
bile: nove righe di nove caratteri che rappresenta-
ranno ognuno una casella del nostro Sudoku.
I caratteri da 1 a 9 indicheranno una casella cui sta-
to assegnato un valore, il carattere '_' indicher, in-
vece, una casella vuota. L'array di array cos creato
verr utilizzato per inizializzare un'istanza della clas-
se Table:
# Carica da file lo stato del Sudoku
def Table.load(filename)
ri = 0
values = []
File.open(filename, "r") do |infile|
while (line = infile.gets)
line.chomp!
row = []
line.each_char do |c|
case c
when '_' :
row.push 0
when '0'..'9':
row.push c
else
raise "#{filename}: carattere inatteso, '#{c}'"
end
end
if row.length != 9
raise "#{filename}: ogni linea deve essere lunga
9 caratteri"
end
values.push(row)
end
if values.length != 9
raise "#{filename}: ci devono essere nove linee"
end end
Table.new values
end
AUMENTIAMO
L'ESPRESSIVIT
Introduciamo altri concetti basilari del Sudoku: ri-
ga, colonna, quadrato, casella, sfruttando l'espressivit
di Ruby. Abbiamo visto che la classe Table conterr i
valori presenti nel nostro Sudoku e anche i valori
scartati per una certa casella; questi dati non saran-
no replicati nelle classi che introdurremo, bens sa-
ranno recuperati da un'istanza della classe Table; da
queste stesse istanze faremo in modo che sia possi-
bile accedere ad ogni riga, colonna, quadrato e ca-
sella sia singolarmente sia eseguendo operazioni per
ognuno di essi tramite i metodi each_row, each_column,
each_square e each_box.
I metodi che permettono l'accesso ai singoli elementi
(la singola riga, la singola colonna e cos via) sono
estremamante semplici e non ci soffermeremo per-
tanto su di questi: si limitano a restituire oggetti crea-
ti all'atto dell'istanziazione di ogni Table. Pi inte-
ressanti sono, invece, i metodi che permettono di
eseguire operazioni sull'insieme dei vari elementi.
Questi metodi saranno quelli che ci permetteranno
di descrivere, con una certa semplicit, i nostri algo-
ritmi per la risoluzione del gioco:
def each_row
(0..8).each do |r|
yield row(r)
end
end
def each_column
(0..8).each do |c|
yield column(c)
end
end
def each_square
(0..2).each do |r|
(0..2).each do |c|
yield square(r,c)
end
end
end
def each_box
(0..8).each do |r|
(0..8).each do |c|
yield(box(r,c))
NOTA
IL GIOCO
DEL SUDOKU
Il Sudoku un gioco molto
semplice: lo scopo
posizionare in una griglia
dei numeri da 1 a 9 in modo
che in ogni quadrato 3x3, in
ogni riga e in ogni colonna
ogni numero sia presente
una e una sola volta.
042-046:072-080 30-09-2008 16:05 Pagina 43
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
44
/Novembre 2008
Tecniche Ruby per la risoluzione di problemi complessi
end
end
end
In quest'ottica scriviamo anche il metodo each_container
che esegue un'operazione su ogni container, cio
sull'insieme di tutte le righe, le colonne e i quadrati.
Questo metodo si comporta come i precedenti sfrut-
tando la lista @containers che contiene, con poca
sorpresa, tutti i container (righe, colonne e quadra-
ti); poich molto simile ai metodi precedenti non
viene riportata, il codice comunque disponibile nel
CD allegato.
LA CLASSE BOX
La classe Box, cos come vedremo per le classi Row,
Column e Square non contiene dati in senso stretto
(i valori delle caselle sono conservati nella classe
Table) ma rappresenta dei legami: in particolare ricorda
i legami di appartenenza di una casella ad ogni con-
tenitore nonch le coordinate della casella. Un'i-
stanza di Boxpu essere vista come una sorta di Proxy
ad una porzione di una Table. Esaminiamo alcuni
metodi, per gli altri, come sempre, vi rimandiamo al
codice allegato:
def can_contains?(value)
(self.value==0 || self.value==value) &&
!row.contains_value?(value) &&
!column.contains_value?(value) &&
!square.contains_value?(value) &&
!@table.exclusions(ri,ci).include?(value)
end
def filled?
self.value!=0
end
# Lista dei possibili valori che pu assumere la casella
# (nel caso sia gi stata assegnata la lista comprende
# il solo valore asegnato)
def possible_values
l=[]
(1..9).each do |n|
if self.can_contains?(n)
l.push(n)
end
end
l
end
I CONTAINER
Le classi Row, Columne Square sono pensate per es-
sere istanziate all'interno della classe Table e non di-
rettamente dall'utilizzatore. Le istanze di ognuna di
queste classi sono infatti legate a doppio filo ad un'i-
stanza di una classa Table; questo perch i nostri con-
tainer sono, proprio come le istanze di Box, delle
semplici viste di una porzione di una Table e han-
no bisogno di un riferimento a questa per accedere
ai valori che in ogni momento sono presenti nello
schema, nonch alla lista dei valori gi scartati per
ogni casella.
Ognuna delle tre classi che stiamo esaminando definisce
un metodo each che permette di eseguire un ciclo
sull'insieme di casella che il nostro container defini-
sce:
class Row
...
def each
(0..8).each do |col|
yield(@table.box(@i,col))
end
end
end
class Column
...
def each
(0..8).each do |row|
yield(@table.box(row,@i))
end
end
end
class Square
...
def each
(0..2).each do |row|
r = @ri*3+row
(0..2).each do |col|
c = @ci*3+col
yield(@table.box(r,c))
end
end
end
end
Tutti i nostri container includono un modulo chia-
mato BoxCollectionche definisce due metodi che si
riveleranno decisamente utili. Il primo
where_can_placeche fornisce una lista di caselle in cui
un determinato valore pu essere inserito:
# lista di elementi dove il numero pu
# essere piazzato (ma non ancora stato messo)
def where_can_place(value)
l = []
NOTA
GENERATORI
DI SUDOKU
Esistono diversi generatori
automatici di Sudoku, ma
secondo il parere degli
appassionati, rimangono
pi interessanti quelli
sviluppati da esseri umani,
il mio consiglio quindi, per
testare i vostri algoritmi di
risoluzione, di usare
schemi che trovate su
riviste o sulla rete.
042-046:072-080 30-09-2008 16:05 Pagina 44
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
45
G
Tecniche Ruby per la risoluzione di problemi complessi
self.each do |box|
if box.can_contains?(value) and box.value==0
l.push(box)
end
end
l
end
Il secondo il metodo contains_value? che indica se
il container contiene gi in qualche sua casella il va-
lore indicato:
def contains_value?(value)
self.each do |box|
if box.value==value
return true
end
end
false
end
PRIMI METODI
DI RISOLUZIONE
Dopo aver posto le basi e aver sviluppato quel lin-
guaggio che andremo a utilizzare possiamo comin-
ciare col definire i primi algoritmi che saranno i pi
semplici, quelli utilizzati da chiunque abbia mai gio-
cato a Sudoku. Creiamo una classe che chiamiamo My-
Solver che conterr un diverso metodo per ogni diversa
tecnica di risoluzione. Le diverse tecniche saranno
poi combinate all'interno del metodo solve.
Il primo metodo che implementeremo prevede di
piazzare un certo valore in una casella se non pu
contenere che quello (ed ovviamente se non stata
gi riempita). Sfruttando il lavoro fatto in preceden-
za esprimere questo concetto diventa decisamente fa-
cile:
# Se un solo valore pu essere piazzato in quella
# casella (non ancora assegnata!) allora lo piazzo
def solve_method_1(table)
puts "{method 1}"
progresses = false
table.each_box do |b|
if !b.filled?
l = b.possible_values
if l.size==1
puts "* Inserisco il valore #{l[0]} nella casella
[#{b.ri},#{b.ci}] perch l'unico valore che pu
contenere"
progresses = (progresses or b.set_value(l[0]))
end
end
end
puts "{progresses? #{progresses}}"
progresses
end
Stampiamo dei messaggi a video per indicare quali so-
no i passi che compiamo nella risoluzione ma so-
prattutto verifichiamo (e restituiamo come risulta-
to) se abbiamo o meno fatto dei progressi. Questo
importante per determinare la condizione d'arresto,
cos come avevamo anticipato quando abbiamo in-
trodotto i metodi set_value ed exclude della classe
Table.
Il secondo metodo consiste nel piazzare un certo va-
lore in una casella se per uno o pi dei suoi container
quella casella l'unica che possa contenere quel va-
lore:
# Se un valore all'interno di un contenitore pu
# andare in una sola casella ce lo metto
def solve_method_2(table)
puts "{method 2}"
progresses = false
table.each_container do |c|
(1..9).each do |n|
l = c.where_can_place(n)
if l.size==1
puts "* Inserisco il valore #{n} nella casella
[#{l[0].ri},#{l[0].ci}] perch l'unica pu essere
contenerlo in #{c}"
progresses = (progresses or l[0].set_value(n))
end
end
end
puts "{progresses? #{progresses}}"
progresses
end
UN METODO
PI COMPLESSO
Implementiamo ora un metodo pi complesso dei
precedenti. Immaginiamo che due caselle all'inter-
no dello stesso contenitore possano assumere solo
due valori, uguali per le due caselle. Nello schema
proposto di seguito, vediamo che le caselle della pri-
ma colonna, prima e seconda riga, possono assu-
mere solo i valori 8 e 9. Quindi, i valori 8 e 9, finiran-
no in queste due caselle anche se non sappiamo an-
cora quale valore occuper una casella e quale oc-
cuper l'altra, quello che per possiamo desumere
che i valori 8 e 9 non potranno essere presenti nel-
le altre caselle del quadrato. Questa tecnica, fra l'altro,
pu essere generalizzata per un numero pi grande
di due caselle (e ovviamente valori).
Ci sono diversi modi per implementare quest'algoritmo.
Nel nostro caso siamo partiti dal considerare che
applicabile a ogni container, quindi abbiamo utiliz-
zo il metodo each_container di table per eseguire le
stesse operazioni per ognuno di questi:
042-046:072-080 30-09-2008 16:05 Pagina 45
ht t p: / / www. i opr ogr ammo. i t
table.each_container do |c|
...
end
All'interno di ogni container esaminiamo ogni ca-
sella e ci interessiamo solo a quelle che possono as-
sumere solo due possibili valori. Inseriamo queste
caselle in una lista conservata nella hash values_sets
sotto la chiave rappresentante l'insieme di possibili
valori che la casella pu assumere. Alla fine di questa
prima fase, values_sets conterr quindi per ogni insieme
di valori possibili la lista di caselle che li possono as-
sumere:
values_sets = Hash.new
c.each do |b|
possible_values = b.possible_values
if possible_values.size == 2
if !values_sets[possible_values]
values_sets[possible_values] = []
end
values_sets[possible_values].push(b)
end
end
A questo punto esaminiamo solamente le liste di due
elementi (l'algoritmo ricerca due caselle che posso-
no assumere gli stessi due valori), per ognuna di que-
ste possiamo concludere che all'interno del contai-
ner solo le due caselle nella lista potranno assumere
i valori usati come chiave, per cui eseguiamo un nuo-
vo ciclo sulle caselle del container ed escludiamo i
valori in esame per tutte le altre:
values_sets.each do |values_set, boxes|
if boxes.size == 2
c.each do |b|
if !boxes.include?(b)
values_set.each do |value|
prog = b.exclude(value)
if prog
puts "* Escludo valore #{value} nella
casella [#{b.ri},#{b.ci}] perch apparir in #{boxes}"
progresses = true
end end
end end
end end
Il metodo completo il seguente:
def solve_method_3(table)
puts "{method 3}"
progresses = false
table.each_container do |c|
values_sets = Hash.new
c.each do |b|
possible_values = b.possible_values
if possible_values.size == 2
if !values_sets[possible_values]
values_sets[possible_values] = []
end
values_sets[possible_values].push(b)
end
end
values_sets.each do |values_set, boxes|
if boxes.size == 2
c.each do |b|
if !boxes.include?(b)
values_set.each do |value|
prog = b.exclude(value)
if prog
puts "* Escludo valore #{value} nella
casella [#{b.ri},#{b.ci}] perch apparir in #{boxes}"
progresses = true
end end
end end
end
end
end
puts "{progresses? #{progresses}}"
progresses
end
ULTERIORI MODIFICHE
A questo punto il nostro risolutore in grado di ri-
solvere completamente da solo i Sudoku pi sem-
plici ma per tutti gli altri si fermer dopo alcuni pas-
saggi. A quel punto potremo compiere qualche pas-
saggio a mano e ridare il Sudoku aggiornato in pasto
al nostro programma oppure studiare nuove tecniche
che gli permettano di superare l'ostacolo.
Federico Tomassetti
SISTEMA M
G
46
/Novembre 2008
Tecniche Ruby per la risoluzione di problemi complessi
Fig. 1: All'interno del quadrato in alto a sinistra i valori 8
e 9 possono essere assegnati solo alle caselle libere
della prima colonna
042-046:072-080 30-09-2008 16:05 Pagina 46
ht t p: / / www. i opr ogr ammo. i t
JAVA
ESEGUIRE UN COMANDO
DI SHELL
String cmd = "ls -al";
Runtime run = Runtime.getRuntime();
Process pr = run.exec(cmd);
pr.waitFor();
BufferedReader buf = new BufferedReader(new
InputStreamReader(pr.getInputStream()));
String line = "";
while ((line=buf.readLine())!=null) {
System.out.println(line);
}
CONFRONTARE DUE OGGETTI
public String[] sortNodes(ArrayList<Node> nodes) {
Node[] sortedNodes = new Node[nodes.size()];
Collections.sort(nodes, new Comparator<Node>() {
public int compare(Node o1, Node o2) {
return o2.priority - o1.priority;
}
});
for (int i=0; i < nodes.size(); i++) {
sortedNodes [i] = nodes.get(i);
}
return sortedNodes ;
}
class Node{
public String name = null;
public int priority;
}
CENTRARE UN TESTO IN SWING
String s;
int width, height;
Graphics g;
FontMetrics fm = getFontMetrics(ftDefault);
Rectangle2D textsize = fm.getStringBounds(s, g);
int xPos = (width - textsize.getWidth()) / 2;
int yPos = (height - textsize.getHeight()) / 2 + fm.getAscent();
g.drawString(s, xPos, yPos);
TROVARE UNA SOTTOSTRINGA
class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() - substring.length();
test:
for (int i = 0; i <= max; i++) {
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++)
!= substring.charAt(k++)) {
continue test;
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" :
"Didn't find it");
}
}
C#
SELEZIONARE TUTTO IL TESTO
CON CTRL-A
private void anyTextBox_KeyPress(object sender,
M TIPS&TRICKS
Novembre 2008/
49
G
Una raccolta di trucchi da tenere a portata di mouse
I trucchi del mestiere
Tips & Tricks
Questa rubrica raccoglie trucchi e piccoli pezzi di codice, frutto dellesperienza di chi programma, che solitamente non
trovano posto nei manuali. Alcuni di essi sono proposti dalla redazione, altri provengono da una ricerca su Internet, altri
ancora ci giungono dai lettori. Chi volesse contribuire, potr inviare i suoi Tips&Tricks preferiti. Una volta selezionati,
saranno pubblicati nella rubrica. Tutti i Tips elencati sono anche presenti nel supporto CD-Rom che accompagna la rivista
049-051:082-083 30-09-2008 16:44 Pagina 49
ht t p: / / www. i opr ogr ammo. i t
System.Windows.Forms.KeyPressEventArgs e)
{
if (e.KeyChar == '\x1')
{
((TextBox)sender).SelectAll();
e.Handled = true;
}
}
LE API DI GOOGLE PER ACCE-
DERE AGLI ALBUM DI PICASA
Prima di utilizzare il codice che di seguito proponiamo,
necessario scaricare le API di Google per .NET e copia-
re i file nel progetto.
I namespace da usare sono:
using Google.GData.Client;
using Google.GData.Photos;
namespace GetPicasaFeed
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.getAtomFeed();
return;
}
private void getAtomFeed()
{
AlbumQuery aQuery = new
AlbumQuery(PicasaQuery.CreatePicasaUri("saschatayefeh"));
PicasaService pService = new
PicasaService("PicasaDemo");
AtomFeed kResultFeed = pService.Query(aQuery);
foreach (AtomEntry entry in kResultFeed.Entries)
{
this.textBox1.AppendText(entry.Title.Text + "\r\n");
foreach (AtomLink eLink in entry.Links)
{
this.textBox1.AppendText(" " +
eLink.HRef.ToString() + "\r\n"); ;
}
}
return;
}
}
}
RIDIMENSIONARE
UNIMMAGINE MANTENENDONE
LE PROPORZIONI
public void ResizeImage(string OriginalFile, string NewFile, int
NewWidth, int MaxHeight, bool OnlyResizeIfWider)
{
System.Drawing.Image FullsizeImage =
System.Drawing.Image.FromFile(OriginalFile);
FullsizeImage.RotateFlip(System.Drawing.
RotateFlipType.Rotate180F lipNone);
FullsizeImage.RotateFlip(System.Drawing.
RotateFlipType.Rotate180F lipNone);
if (OnlyResizeIfWider)
{
if (FullsizeImage.Width <= NewWidth)
{
NewWidth = FullsizeImage.Width;
}
}
int NewHeight = FullsizeImage.Height * NewWidth /
FullsizeImage.Width;
if (NewHeight > MaxHeight)
{
NewWidth = FullsizeImage.Width * MaxHeight /
FullsizeImage.Height;
NewHeight = MaxHeight;
}
System.Drawing.Image NewImage = FullsizeImage.Get
ThumbnailImage(NewWidth, NewHeight, null, IntPtr.Zero);
FullsizeImage.Dispose();
// Salva limmagine ridimensionata
NewImage.Save(NewFile);
}
COMPRIMERE E DECOMPRIMERE
UN ARRAY DI BYTE
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.IO;
using System.Collections;
namespace Utilities
{
class Compression
{
public static byte[] Compress(byte[] data)
{
MemoryStream ms = new MemoryStream();
DeflateStream ds = new DeflateStream(ms,
CompressionMode.Compress);
ds.Write(data, 0, data.Length);
ds.Flush();
ds.Close();
TIPS&TRICKS M
G
50
/Novembre 2008
Una raccolta di trucchi da tenere a portata di mouse
049-051:082-083 30-09-2008 16:44 Pagina 50
ht t p: / / www. i opr ogr ammo. i t
return ms.ToArray();
}
public static byte[] Decompress(byte[] data)
{
const int BUFFER_SIZE = 256;
byte[] tempArray = new byte[BUFFER_SIZE];
List<byte[]> tempList = new List<byte[]>();
int count = 0, length = 0;
MemoryStream ms = new MemoryStream(data);
DeflateStream ds = new DeflateStream(ms,
CompressionMode.Decompress);
while ((count = ds.Read(tempArray, 0, BUFFER_SIZE))>0)
{
if (count == BUFFER_SIZE)
{
tempList.Add(tempArray);
tempArray = new byte[BUFFER_SIZE];
}
else
{
byte[] temp = new byte[count];
Array.Copy(tempArray, 0, temp, 0, count);
tempList.Add(temp);
}
length += count;
}
byte[] retVal = new byte[length];
count = 0;
foreach (byte[] temp in tempList)
{
Array.Copy(temp, 0, retVal, count, temp.Length);
count += temp.Length;
}
return retVal;
}
}
}
UNA CLASSE PER ORDINARE
UNA LISTA GENERICA
public class Item
{
public Item(string term, int freq)
{
_term = term;
_freq = freq;
}
private string _term;
public string Term
{
get
{ return _term; }
set
{ _term = value; }
}
private int _freq;
public int Freq
{
get
{ return _freq;
}
set { _freq = value; }
}
}
public class ItemComparer:IComparer<Item>
{
#region IComparer<Item> Members
public int Compare(Item x, Item y)
{
return y.Freq - x.Freq;
//ordine decrescente
//return x.Freq - y.Freq;
//ordine crescente
}
#endregion
}
static void Main()
{
List<Item> items = new List<Item>();
items.Sort(new ItemComparer());
}
OTTENERE LA LISTA
DEI PROCESSI ATTIVI
Console.WriteLine("ID:\tNome del processo:");
Console.WriteLine("--\t------------");
foreach (System.Diagnostics.Process process in
System.Diagnostics.Process.GetProcesses())
Console.WriteLine("{0}\t{1}", process.Id, process.ProcessName);
UNA PROCEDURA PER
RIMUOVERE NODI XML VUOTI
public static void RimuoviNodiXMLVuoti(XmlDocument doc)
{
XmlNodeList nodes = doc.SelectNodes("//node()");
foreach (XmlNode node in nodes)
if ((node.Attributes.Count == 0) && (node.ChildNodes.Count
== 0))
node.ParentNode.RemoveChild(node);
}
M TIPS&TRICKS
Novembre 2008/
51
G
Una raccolta di trucchi da tenere a portata di mouse
049-051:082-083 30-09-2008 16:44 Pagina 51
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
52
/Novembre 2008
Sviluppare un moderno editor di testi
VB.NET 2008
E I WORD PROCESSOR
IN QUESTARTICOLO VENGONO SVELATI ALCUNI SEGRETI CHE SONO ALLA BASE DEI WORD
PROCESSOR DI MICROSOFT. IN PARTICOLARE MOSTREREMO COME IMPLEMENTARE LE
CLASSICHE FUNZIONI DI UN EDITOR, SFRUTTANDO IL CORE DEL .NET FRAMEWORK
C
on Visual Basic possibile creare
qualsiasi applicazione per scopi pro-
fessionali o ludici. Le difficolt di
questo strumento di sviluppo sono piena-
mente compensate da intelligenti agevola-
zioni, quali una documentazione completa
e ricca desempi, gli innumerevoli wizard
disponibili nellambiente integrato di lavo-
ro (IDE), le particolari tecnologie che aiu-
tano a programmare in maniera pi rapida
e le nuove funzionalit e caratteristiche del
linguaggio che, di solito, evitano di accede-
re alle funzioni a basso livello del sistema
operativo (API).
Lobiettivo principale di questo articolo quel-
lo di illustrare la programmazione di un text
editor completo tramite Visual Basic.NET 2008.
Le funzionalit rese disponibili sono, per alcu-
ni versi, pi sofisticate del classico Blocco note
di Windows, ma sicuramente meno avanzate di
Microsoft Word. Gli spunti che si possono trar-
re da questo articolo consentiranno, tuttavia,
di comprendere i meccanismi di base che rego-
lano la programmazione degli elaboratori testi
ed inoltre di sviluppare ulteriori e pi comples-
se funzionalit. Quello che si vuole fare, dun-
que, creare un text editor che abbia alcune
caratteristiche grafiche tipiche dei word pro-
cessor moderni, ossia menu a tendina con
icone identificative della voce di menu e barra
degli strumenti per laccesso veloce alle varie
funzionalit.
KICK OFF
DEL PROGETTO
Una volta avviato VB.NET, dovete fare clic sul
menu File-New-Project, selezionare Windows
Application nella sezione Templates, digitare Ap-
punti in Name e, poi, fare clic sul pulsante OK.
Da questo momento in poi comincia la fase di
programmazione vera e propria. Selezionate,
dunque, il controllo MainMenu sulla toolbox e
trascinatelo sulla form per cominciare ad edita-
re i vari menu da creare. Poich il menu ha una
veste grafica scarna, si cercher subito di ren-
derlo pi accattivante inserendo alcune icone
prima delle singole voci. Si tratta di unopera-
zione non troppo complessa, ma sicuramente
non automatica. Prima di tutto bisogna espandere
la regione che contiene il codice di progettazio-
ne generato da Windows Form e inserire la pro-
priet OwnerDraw ponendola uguale a True in
ogni sezione dedicata ai vari sottomenu (cio i
menu di secondo livello) tra lindice del menu e
il suo nome. Per la prima voce Nuovo le impo-
stazioni saranno, ad esempio, le seguenti:
Me.mnuNuovo.Index = 0
Me.mnuNuovo.OwnerDraw = True
Me.mnuNuovo.Text = "Nuovo"
Infatti, solo se la propriet OwnerDraw impo-
stata su True, potete personalizzare la grafica del
menu per creare visualizzazioni particolari via
codice.
Il secondo controllo da utilizzare e da trascina-
re sulla form ImageList per raggruppare nel-
linsieme Images tutte le icone che verranno uti-
lizzate sia nel menu personalizzato che nella bar-
ra degli strumenti.
Una volta ultimata questa operazione un po te-
diosa possiamo finalmente mettere mani al co-
dice in maniera massiccia inserendolo negli even-
ti MeasureIteme DrawItem. Di seguito viene ri-
portata parte della procedura disegnaMenu() che
consente la gestione unificata di tutti gli eventi
DrawItemdei menu.
Public Sub disegnaMenu(...) _
Handles mnuNuovo.DrawItem, ...,
mnuSuccessivo.DrawItem
Dim SottoMenu As MenuItem =
CType(sender, MenuItem)
Dim Immagine As Integer
Dim coloreTesto As Color =
SystemColors.MenuText
J CD J WEB
VBNET editor.rar
cdrom.ioprogrammo.it
Conoscenze richieste
Visual Studio e VB.NET
Software
Windows XP Service
Pack 2, Windows
Server 2003 o
Windows Vista e
Visual Studio 2008
versione Standard o
Professional
Impegno

Tempo di realizzazione
REQUISITI
053-058:072-080 30-09-2008 16:15 Pagina 52
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
53
G
Sviluppare un moderno editor di testi
Dim posizioneTesto As New
RectangleF(e.Bounds.Left + 20, _
e.Bounds.Top + 2, e.Bounds.Right,
e.Bounds.Bottom - 2)
Dim formattaTesto As New StringFormat()
Dim tabStops() As Single = {0}
formattaTesto.SetTabStops
(larghezzaMassimaMenu, tabStops)
If (ShowKeyboardCues) Then
formattaTesto.HotkeyPrefix =
Drawing.Text.HotkeyPrefix.Show
Else
formattaTesto.HotkeyPrefix =
Drawing.Text.HotkeyPrefix.Hide
End If
Dim selected As Boolean = False
selected = ((e.State And
DrawItemState.Selected) = _
DrawItemState.Selected)
If selected Then
e.DrawBackground()
coloreTesto = SystemColors.HighlightText
Else
e.Graphics.FillRectangle
(SystemBrushes.Menu, e.Bounds)
End If
...
End Sub
Sia nel caso di MeasureItemche di DrawItem
stata, dunque, definita una procedura per abbi-
nare, con la parola chiave Handle, levento cor-
rente a tutte le voci di menu, consentendo di ri-
sparmiare molto tempo in fase di scrittura del
programma.
Essendo stata messa a fattor comune la stessa
procedura, non dovrete ripeterla nei vari eventi
dei menu.
In effetti lalternativa agli array di controlli di
Visual Basic 6.0, che in Visual Basic.NET non esi-
stono pi.
Se osservate la procedura ToolBar1_ButtonClick,
che gestisce appunto la barra degli strumenti,
interessante notare il metodo PerfomClick che
consente di simulare un clic utente attivando la
voce di menu relativa. Ad esempio mnuNuo-
vo.PerformClick() lancia il menu Nuovo, come
se si facesse direttamente clic su di esso.
Private Sub ToolBar1_ButtonClick(...) Handles
ToolBar1.ButtonClick
If e.Button Is Nuovo Then
mnuNuovo.PerformClick()
ElseIf e.Button Is Apri Then
mnuApri.PerformClick()
...
mnuSuccessivo.PerformClick()
ElseIf e.Button Is PrimoPiano Then
mnuPrimoPiano.PerformClick()
End If
End Sub
LE DIMENSIONI
DEL MENU
Levento MeasureItemviene attivato tutte le vol-
te che un menu sta per essere disegnato. Il suo
obiettivo quello di accertare la dimensione del
menu stesso ed qui, perci, che dovete defi-
nirne altezza e larghezza per poi passarle a Win-
dows.
Private Sub dimensionaMenu(ByVal sender As
System.Object, ByVal e As
System.Windows.Forms.MeasureItemEventArgs) _
Handles mnuNuovo.MeasureItem, ...,
mnuSuccessivo.MeasureItem
Dim SottoMenu As MenuItem =
CType(sender, MenuItem)
Dim dimTesto As Size
Dim formattaTesto As New StringFormat()
Dim altezzaMassimaMenu As Integer
If (ShowKeyboardCues) Then
formattaTesto.HotkeyPrefix =
Drawing.Text.HotkeyPrefix.Show
Else
formattaTesto.HotkeyPrefix =
Drawing.Text.HotkeyPrefix.Hide
End If
dimTesto = e.Graphics.MeasureString
(SottoMenu.Text, aFont).ToSize()
larghezzaMassimaMenu = Math.Max
(larghezzaMassimaMenu, dimTesto.Width + _
SystemInformation.SmallIconSize.Width + 4)
e.ItemWidth = larghezzaMassimaMenu
altezzaMassimaMenu =
SystemInformation.SmallIconSize.Height + 2
Fig. 1: Le icone inserite nel controllo ImageList
NOTA
UTILIZZARE
MATH.MAX()
Il metodo Max() di Visual
Basic.NET serve a
determinare qual il valore
pi elevato tra due numeri e
a restituire il risultato alla
variabile a cui uguagliato.
La libreria di riferimento a
cui appartiene Max()
quella matematica che
appartiene alla classe
Math().
053-058:072-080 30-09-2008 16:15 Pagina 53
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
54
/Novembre 2008
Sviluppare un moderno editor di testi
e.ItemHeight = altezzaMassimaMenu
End Sub
La propriet SmallIconSize della classe System-
Informationrestituisce in Height laltezza delle ico-
ne piccole del sistema operativo. Tenendo con-
to di due pixel di spaziatura possibile definire
laltezza di ogni singolo elemento del menu.
Per la larghezza il procedimento adottato pi com-
plicato, infatti simmagazzina in una variabile
globale la larghezza del menu ottenuta misu-
rando tutte le voci dello stesso gruppo, ad esem-
pio del menu File, e tutte le volte si valuta se il
valore pi grande quello corrente o quello sal-
vato. In questo caso si utilizzata la funzione
Max() e inizialmente la classe Graphics per de-
terminare la dimensione della voce corrente. La
variabile larghezzaMassimaMenu viene reim-
postata a 0 tutte le volte che viene attivato levento
Select sui menu di primo livello. La procedura
la azzeraLarghezzaMenu().
Private Sub azzeraLarghezzaMenu(...) _
Handles mnuFile.Select, mnuModifica.Select,
mnuHelp.Select
larghezzaMassimaMenu = 0
End Sub
DISEGNARE IL MENU
Per disegnare il menu dellapplicazione dovete scri-
vere la procedura disegna Menu() che costruisce
le componenti grafiche dopo che ne sono gi sta-
te individuate le dimensioni. In effetti, se com-
mentate lintera procedura ed eseguite
lapplicazione si pu notare che il menu viene
creato ugualmente, ma mancano totalmente ico-
ne e descrizioni essendo visibili solo dei riquadri
bianchi. Il fulcro della procedura la Select Case
finale, i metodi DrawImageUnscaled e Draw-
String necessari rispettivamente per individua-
re limmagine giusta nel controllo ImageList, per
disegnare limmagine stessa e per disegnare la
corrispondente scritta di menu.
Select Case SottoMenu.Text
Case "Nuovo"
Immagine = 0
Case "Apri..."
Immagine = 1
...
...
Case "Salva con nome..."
Immagine = 11
Case Else
Immagine = -1
End Select
If Immagine > -1 Then
e.Graphics.DrawImageUnscaled(imgList.Images
(Immagine), e.Bounds.Left + 1, e.Bounds.Top + 1)
End If
e.Graphics.DrawString(SottoMenu.Text, aFont, _
New SolidBrush(coloreTesto), posizioneTesto,
formattaTesto)
Se un menu non ha alcuna icona abbinata, come
Informazioni su, viene impostato il valore di
default 1 e, quindi, non viene richiamato il co-
struttore dellimmagine. Fate attenzione che ogni
voce tra virgolette dopo i vari Case deve corri-
spondere esattamente alla propriet text delli-
tem di menu corrispondente. Ad esempio Case
Nuovo corrisponde al contenuto della pro-
priet mnuNuovo.text.
IL TEXT EDITOR
VERO E PROPRIO
Una volta definita la parte grafica, impostando an-
che una TextBox multiline per consentire la scrit-
tura del testo, necessario che scriviate il codi-
ce dedicato alla gestione dei documenti che ven-
gono creati.
In questapplicazione si fa largo uso degli oggetti
di tipo Common Dialog, che forniscono una se-
rie di finestre di dialogo standard nelle quali
possibile aprire, chiudere, salvare e stampare fi-
le o anche selezionare colori e tipi di caratteri.
In particolare lapertura di un documento esi-
stente definita nellevento mnuApri_Click() do-
ve viene attivato il metodo ApreDialo-
go.ShowDialog() per visualizzare la schermata
standard Apri di Windows.
If Risposta <> vbCancel Then
If ApreDialogo.ShowDialog() = vbOK Then
Call ApreilFile(ApreDialogo.FileName)
Me.Text = ApreDialogo.FileName
Salvato = True
End If
End If
Nel caso in cui lutente scelga il pulsante Apri il
risultato uguale alla costante vbOK e, quindi,
si prosegue richiamando la procedura ApreilFile
alla quale viene passato il parametro ApreDial-
ogo.FileName, che corrisponde al nome del file scel-
to.
Protected Sub ApreilFile(ByVal NomeDelFile As
String)
Dim legge As StreamReader
Dim riga As String
Dim finito As Boolean = False
NOTA
TEXTBOX
MULTILINE
Il controllo TextBox in
modalit multiline in
grado di gestire la scrittura
di testo su pi righe. Inoltre,
se viene impostata la
propriet ScrollBars
possibile aggiungere barre
di scorrimento sia
orizzontali che verticali a
controlli TextBox di grandi
dimensioni.
053-058:072-080 30-09-2008 16:15 Pagina 54
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
55
G
Sviluppare un moderno editor di testi
txtAppunti.Text = ""
legge = New StreamReader(NomeDelFile)
Try
While Not finito
riga = legge.ReadLine()
If riga Is Nothing Then
finito = True
Else
txtAppunti.Text = txtAppunti.Text &
riga & vbCrLf
End If
End While
Finally
legge.Close()
End Try
End Sub
Come si vede dal listato dovete utilizzare la clas-
se StreamReader() del Framework .NET per leg-
gere i caratteri dal file selezionato e, via via tra-
mite il ciclo While Not finito, riportati nella textbox
txtAppunti.Text. Si ricorda che StreamReader
presente nello spazio dei nomi System.IO. In al-
ternativa potreste ricorrere alla funzione File
Open() che consente di aprire un file per linput
o loutput.
La scrittura del file avviene tramite la procedura
SalvailFile() che pu essere richiamata in pi
punti, ossia premendo il pulsante Salva oppure
ogniqualvolta lutente decida di uscire dal file
corrente, anche cercando di chiudere lintera ap-
plicazione. Questultima possibilit si verifica
nellevento Form1_Closing() che interviene in
tutti i casi in cui si cerca di chiudere lapplicazione:
con Alt+F4, cliccando sul pulsante di default di Win-
dows Chiudi da menu o sulla X in alto a destra.
Private Sub Form1_Closing(...) Handles
MyBase.Closing
Dim Cancel As Short = eventArgs.Cancel
Dim Risposta As Byte = vbYes
If Len(txtAppunti.Text) > 0 And Not Salvato
Then
Risposta = MsgBox("Il testo stato
cambiato?" & _
vbCrLf & "Salvare i cambiamenti?", _
MsgBoxStyle.YesNoCancel +
MsgBoxStyle.Question)
If Risposta = vbYes Then
If Me.Text = "Appunti" Then
If ChiudeDialogo.ShowDialog() =
vbOK Then
Call
SalvailFile(ChiudeDialogo.FileName)
End If
Else
Call SalvailFile(Me.Text)
End If
End If
End If
If Risposta = vbCancel Then
Cancel = True
End If
eventArgs.Cancel = Cancel
End Sub
La procedura SalvailFile() interviene se il meto-
do ChiudeDialogo.ShowDialog(), destinato a vi-
sualizzare la schermata standard Salva con
nome di Windows, uguale alla costante vbOK,
e cio se lutente ha premuto il pulsante Salva.
Protected Sub SalvailFile(ByVal NomeDelFile As
String)
Dim scrive As StreamWriter
Dim i As Integer
scrive = New StreamWriter(NomeDelFile)
Try
For i = 0 To txtAppunti.Lines.Length - 1
Call scrive.WriteLine(txtAppunti.Lines(i))
Next
Finally
scrive.Close()
End Try
End Sub
In SalvailFile ancora una volta viene utilizzata
una potente classe del Framework .NET, la
StreamWriter, che pemette di scrivere tutto ci
che presente nella textbox in un file chiamato
NomeDelFile. poi il ciclo For...Next, basato sul
metodo WriteLine, che si prende cura di fare ci.
LA STAMPA DEL TESTO
Ogni text editor che si rispetti non pu proprio fa-
re a meno delle funzioni di stampa. Una possibilit
per implementarle quella di utilizzare il con-
trollo CrystalReportViewer, anche se per questo
programma stata scelta una strada alternati-
va. Innanzitutto si consideri il codice che per-
mette dimpostare le caratteristiche della pagina
da inviare alla stampante. Nella procedura im-
postaPagina() viene definita una nuova istanza
della classe PageSetupDialog() che fornisce tut-
te le opzioni standard per gestire margini, orien-
tamento, alimentazione e formato della pagina.
Private Sub impostaPagina()
Dim miaPagina As New PageSetupDialog()
With miaPagina
.Document = stampaDoc
.PageSettings =
stampaDoc.DefaultPageSettings
NOTA
IL CONTROLLO
RICHTEXTBOX
Il controllo RichTextBox pu
essere utilizzato in
alternativa alla TextBox per
visualizzare, immettere e
modificare testo formattato.
Oltre alle funzionalit offerte
dal controllo TextBox,
RichTextBox consente di
visualizzare tipi di carattere,
colori e collegamenti, di
caricare da un file un testo
e immagini incorporate e di
trovare caratteri specificati.
Viene generalmente
utilizzato per fornire
funzionalit di
visualizzazione e modifica
del testo simili a quelle
offerte da programmi di
elaborazione testi quale
Microsoft Word.
053-058:072-080 30-09-2008 16:15 Pagina 55
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
56
/Novembre 2008
Sviluppare un moderno editor di testi
End With
If miaPagina.ShowDialog = vbOK Then
stampaDoc.DefaultPageSettings =
miaPagina.PageSettings
End If
End Sub
Il documento rappresentato dalla variabile og-
getto stampaDoc che fa riferimento a unistan-
za di una classe che pu generare eventi. Ecco
perch viene dichiarata come segue: Private With-
Events stampaDoc As New PrintDocument()
Ricordate anche che la propriet DefaultPage-
Settings serve ad attribuire le impostazioni pre-
definite a tutte le pagine da stampare. Successi-
vamente, quando vengono accettate le perso-
nalizzazioni dellutente, viene modificato il de-
fault uguagliandolo ai nuovi settings: stam-
paDoc.DefaultPageSettings = impostaPagi-
na.PageSettings
Unaltra importante procedura quella che ge-
stisce limpostazione delle stampanti.
Private Sub mostraStampanti()
Dim mieStampanti As New PrintDialog()
mieStampanti.Document = stampaDoc
If mieStampanti.ShowDialog = vbOK Then
stampaDoc.Print()
End If
End Sub
In questo caso mostraStampanti() attiva una
nuova istanza della classe PrintDialog che permette
di visualizzare la finestra di dialogo delle stam-
panti, dove sono presenti le opzioni per indiriz-
zare la stampa su file, per fascicolare le copie e per
definire quante copie inviare in stampa. Se si fa
clic su OK viene inviata la pagina in stampa con
stampaDoc.Print()
ANTEPRIMA DI
STAMPA
Non sempre i text editor hanno a disposizione
la funzionalit di anteprima di stampa, ad esem-
pio Microsoft Notepad non ce lha, ma in questo
caso si pensato di contemplarla. La classe chia-
ve questa volta PrintPreviewDialog(). Anche in
questo caso possibile inviare direttamente la
pagina alla stampante.
Private Sub anteprimaStampa()
Dim anteprima As New PrintPreviewDialog()
Try
anteprima.Document = stampaDoc
anteprima.ShowDialog()
Catch exp As Exception
MsgBox("Si verificato un errore durante " & _
"lanteprima di stampa. Probabilmente " & _
"la stampante non connessa o non
accessibile.", MsgBoxStyle.OKOnly +
MsgBoxStyle.Critical)
End Try
End Sub
Infine potete creare la procedura per la stampa
diretta della pagina, che simile alla mostra-
Stampanti() anche se procede subito allinvio
della stampa.
Private Sub Stampa()
Dim stampa As New PrintDialog()
Try
stampa.Document = stampaDoc
stampaDoc.Print()
Catch exp As Exception
MsgBox("Si verificato un errore mentre si
tentava " & "di stampare il documento.
Verificare" & _
"che una stampante sia disponibile " & _
"ed accessibile.", MsgBoxStyle.OkOnly +
MsgBoxStyle.Critical)
End Try
End Sub
IL CUORE DELLE
FUNZIONALIT
DI STAMPA
Per poter stampare non sufficiente il codice
mostrato fino ad ora, ma anche necessario pro-
grammare levento stampaDoc_PrintPage() in
maniera dettagliata. Vediamone alcuni elemen-
ti chiave. La variabile statica ultimoCarattere-
Stampato tiene traccia dellultimo carattere stam-
pato, ecco perch dovete dichiararla Static; so-
lo in questo modo il successivo evento PrintPage
pu fare riferimento ad essa. In seguito viene di-
chiarato il font che si vuole visualizzare in stam-
pa con Dim font As New Font("Microsoft Sans
Serif", 10). Vengono inoltre inizializzate le varia-
bili che tengono traccia dei confini dellarea ret-
tangolare di stampa:
Dim altezzaArea, larghezzaArea, margineSX,
margineAlto As Int32
With stampaDoc.DefaultPageSettings
altezzaArea = .PaperSize.Height -
.Margins.Top - .Margins.Bottom
larghezzaArea = .PaperSize.Width -
.Margins.Left - .Margins.Right
margineSX = .Margins.Left X coordinate
NOTA
CHIUSURA
DELLAPPLICAZIONE
In Visual Basic.NET le
applicazioni possono essere
chiuse utilizzando il metodo
Exit() applicato alla classe
Application oppure tramite il
metodo Me.Close().
Questultima soluzione si fa
preferire se si vuole
scatenare levento Closing
della form, nel quale
possibile inserire gli ultimi
controlli di convalida prima
di fare il kill di tutte le
risorse. Qualora questi
controlli imponessero di
bloccare luscita
dallapplicazione
necessario impostare la
propriet Cancel delloggetto
FormClosingEventArgs a
True.
053-058:072-080 30-09-2008 16:15 Pagina 56
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
57
G
Sviluppare un moderno editor di testi
margineAlto = .Margins.Top Y coordinate
End With
Poich lorientamento predefinito della pagina
verticale, necessaria una procedura per ruo-
tarla quando viene scelta limpostazione oriz-
zontale. Questo viene fatto tramite la variabile
di appoggio varTemp.
If stampaDoc.DefaultPageSettings.Landscape
Then
Dim varTemp As Int32
varTemp = altezzaArea
altezzaArea = larghezzaArea
larghezzaArea = varTemp
End If
Dovete, poi, dichiarare ed inizializzare la varia-
bile areaStampaRet che definisce larea rettan-
golare da mandare in stampa: Dim areaStam-
paRet As New RectangleF(margineSX, margin-
eAlto, larghezzaArea, altezzaArea)
Successivamente dovete istanziare la classe String-
Format che incapsula le informazioni per il layout
del testo, come lallineamento e la spaziatura tra
le linee. Inoltre il suo utilizzo impone a Mea-
sureString e a DrawString di utilizzare solo un
numero intero di righe quando viene stampata ogni
pagina, non considerando eventuali valori fra-
zionari: Dim formato As New StringFor-
mat(StringFormatFlags.LineLimit)
Il metodo Graphics.MeasureString deve essere
richiamato per determinare il numero dei ca-
ratteri che devono entrare nellarea rettangola-
re di stampa, dove la variabile caratteriPerPagi-
na indica il numero di caratteri che viene inserito
nella pagina corrente, mentre righePerPagina
indica il numero di righe che viene inserito nel-
la pagina corrente. Questultima variabile, in ef-
fetti, non qui utilizzata, ma comunque ri-
chiesta dal metodo Graphics.MeasureString().
Infine il primo parametro che contiene la fun-
zione Mid utilizzato per mandare in stampa la
parte del documento che non era stata stampa-
ta nella pagina precedente grazie alla variabile
ultimoCarattereStampato che mantiene i valo-
ri, essendo di tipo Static.
Dim righePerPagina, caratteriPerPagina As Int32
e.Graphics.MeasureString(Mid(txtAppunti.Text, _
ultimoCarattereStampato + 1), font, _
New SizeF(larghezzaArea, altezzaArea),
formato, caratteriPerPagina, righePerPagina)
Il metodo Graphics.DrawString, invece, vi per-
metter di scrivere il testo nellarea di stampa
specificata, essendo anche qui necessario indi-
care la funzione Mid, il font, il colore del testo,
appunto larea di stampa areaStampaRet e infi-
ne il formato definito in precedenza con la fun-
zione StringFormat().
e.Graphics.DrawString(Mid(txtAppunti.Text, _
ultimoCarattereStampato + 1), font, _
Brushes.Black, areaStampaRet, formato)
Lultimo elemento importante da segnalare la
propriet HasMorePages che indica alla proce-
dura di stampa se deve essere stampata una pa-
gina aggiuntiva oppure no.
If ultimoCarattereStampato <
txtAppunti.Text.Length Then
e.HasMorePages = True
Else
e.HasMorePages = False
ultimoCarattereStampato = 0
End If
Per verificare la lunghezza della textbox txtAp-
punti stata utilizzata la propriet Length ap-
plicata a txtAppunti.Text. Si tratta di una carat-
teristica del Framework .NET e che non era sup-
portata con le versioni precedenti di Visual Basic
dove sarebbe stato necessario utilizzare la funzione
Len per ottenere lo stesso risultato:
Len(txtAppunti.Text).
TAGLIA,
COPIA E INCOLLA
Le operazioni di copia, taglia e incolla sono uno
standard di Windows fin dalle prime versioni del
sistema operativo di Microsoft ed , opportuno
che vengano abilitate anche nel text editor. Le
possibilit disponibili sono almeno due e ver-
ranno esaminate entrambe.
Per tagliare il testo possibile utilizzare il meto-
do Clipboard.SetDataObject() al quale devono
essere passati due parametri: il testo seleziona-
to da mettere negli appunti; un flag booleano,
che se impostato a True permette di mantene-
re negli appunti di sistema il testo salvato anche
dopo la chiusura dellapplicazione. Se imposta-
to a False i dati verranno eliminati dalla clipboard
alla chiusura dellapplicazione.
Private Sub mnuTaglia_Click(...) Handles
mnuTaglia.Click
Clipboard.SetDataObject(txtAppunti.SelectedText,
True)
txtAppunti.SelectedText = ""
End Sub
053-058:072-080 30-09-2008 16:15 Pagina 57
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
58
/Novembre 2008
Sviluppare un moderno editor di testi
Per impostare la funzione di copia il codice lo
stesso di taglia, escludendo per la cancellazio-
ne del testo selezionato.
Private Sub mnuCopia_Click(...) Handles
mnuCopia.Click
Clipboard.SetDataObject(txtAppunti.SelectedText,
True)
End Sub
Infine, per incollare il testo si deve utilizzare il
metodo Clipboard.GetDataObject() che restitui-
sce il valore in un oggetto IdataObject che pote-
te testare per capire se ci che si trova negli ap-
punti del testo o unimmagine.
Private Sub mnuIncolla_Click(...) Handles
mnuIncolla.Click
Dim Appunti As IDataObject =
Clipboard.GetDataObject
Dim oggettoAppunti As Object
oggettoAppunti =
Appunti.GetData(DataFormats.Text, False)
If Not (oggettoAppunti Is Nothing) Then
txtAppunti.SelectedText =
CStr(oggettoAppunti)
End If
End Sub
Nelloggetto oggettoAppunti viene immagazzi-
nato il valore presente in quel momento negli
appunti tramite Appunti.GetData(DataFormats.
Text, False), lasciando il secondo parametro a
False per impedire una conversione automatica
del formato dei dati presenti nella clipboard di
Windows. Dopo il test della presenza di dati av-
viene lincollaggio vero e proprio del testo con
txtAppunti.SelectedText = CStr(oggetto Appunti)
Esistono anche altri metodi che possono essere
utilizzati per le operazioni di copia, taglia e in-
colla. Questa seconda modalit quella che de-
ve essere utilizzata se si utilizza una funzione di
annulla.
Nel caso di Taglia e Copia basta ricorrere rispet-
tivamente ai metodi Cut() e Copy() che interagi-
scono direttamente con la clipboard.
Private Sub mnuTaglia_Click(...) Handles
mnuTaglia.Click
txtAppunti.Cut()
End Sub
Private Sub mnuCopia_Click(...) Handles
mnuCopia.Click
txtAppunti.Copy()
End Sub
Per incollare i dati, invece, dovete utilizzare un al-
goritmo differente che prima verifica se c qual-
cosa nella clipboard da incollare esaminando se
Clipboard.Get-DataObject().GetDataPresent
(DataFormats.Text) uguale a True. Successiva-
mente viene anche verificato se lutente ha se-
lezionato del testo e si richiede conferma per
laggiornamento. Se okPaste False significa che
non si vuole procedere alloverwrite e, quindi, i
dati non vengono incollati con il metodo Paste().
Private Sub mnuIncolla_Click(...) Handles
mnuIncolla.Click
Dim okPaste As Boolean = True
If Clipboard.GetDataObject().
GetDataPresent(DataFormats.Text) Then
If txtAppunti.SelectionLength > 0 Then
If MsgBox("Vuoi incollare sopra la
selezione corrente?",
MsgBoxStyle.YesNo +
MsgBoxStyle.Question, "Appunti") = vbNo Then
okPaste = False
End If
End If
If okPaste Then txtAppunti.Paste()
End If
End Sub
RICERCHE ALLINTERNO
DEL TESTO
Lonnipresente Find ha unindubbia utilit e,
se possibile, deve sempre essere fornita quando
si progetta un text editor. Lalgoritmo proposto
piuttosto semplice e ha due punti importanti da
evidenziare. Il primo quello dove viene fatta la
ricerca vera e propria del testo tramite la fun-
zione InStr() che riceve tre parametri: la posi-
zione di partenza del cursore, il testo su cui effettare
la ricerca e il testo da ricercare.
Il testo ricercato viene evidenziato a partire dal
carattere indicato dalla propriet SelectionStart
e per una lunghezza pari alla SelectionLength. Il
metodo ScrollToCaret consente, poi, di scrollare
il contenuto della textbox fino al punto in cui il
cursore ha trovato la stringa ricercata: fonda-
mentale per rendere immediatamente visibile
allutente il risultato della Find.
posizioneParola = InStr(Partenza,
txtAppunti.Text, parolaCercata)
If parolaCercata.Length <> 0 Then
If posizioneParola Then
txtAppunti.SelectionStart =
posizioneParola - 1
txtAppunti.SelectionLength =
parolaCercata.Length
txtAppunti.ScrollToCaret()
053-058:072-080 30-09-2008 16:15 Pagina 58
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
59
G
Sviluppare un moderno editor di testi
Else
posizioneParola = 0
Partenza = 1 : parolaPrecedente = ""
MsgBox("Stringa non trovata",
MsgBoxStyle.Information)
End If
End If
Il secondo punto quello che attribuisce il giu-
sto valore ai parametri eventualmente per pro-
seguire nella ricerca. , infatti, la variabile Parten-
za che viene inzializzata diversamente a secon-
da che si tratti di una ricerca inziale o successi-
va.
parolaPrecedente = parolaCercata
If Not TrovaSuccessivo Then
parolaCercata = InputBox("Digitare la
parola da ricercare", _ "Trova", parolaCercata)
End If
If Not (parolaPrecedente Is Nothing) Then
If parolaPrecedente = parolaCercata Then
per ricerca successiva
Partenza = IIf(posizioneParola +
parolaCercata.Length = 0, Partenza, _
posizioneParola +
parolaCercata.Length)
Else
posizioneParola = 0
Partenza = 1
End If
End If
Viene, infine, fatto largo utilizzo delle variabili
statiche per mantenere i valori che altrimenti
verrebbero persi.
Si ricorda che una variabile Static ha visibilit
solo allinterno della routine che la creata, ma la
persistenza del suo valore in memoria pari al-
la durata dellapplicazione. Fintanto che il pro-
gramma non viene chiuso queste variabili sono
utilizzabili e mantengono il loro valore.
Static posizioneParola As Integer,
parolaCercata As String = ""
Static Partenza As Integer = 1,
parolaPrecedente As String
Infine anche possibile cercare con il tasto F3, con
licona sulla barra degli strumenti e con Trova
Successivo da menu loccorrenza successiva del
testo ricercato. Per fare questo stato sufficien-
te gestire il flag globale TrovaSuccessivo nelle pro-
cedure mnuSuccessivo_Click e mnuTrova_Click.
If txtAppunti.Text.Length > 0 Then
TrovaSuccessivo = True
mnuTrova.PerformClick()
TrovaSuccessivo = False
End If
ANNULLARE
UNOPERAZIONE
Anche in questo caso ci si trova di fronte ad una
funzionalit importante per lutente finale.
Per fortuna Visual Basic.NET aiuta notevolmen-
te i programmatori e impone la scrittura di poche
righe di codice per implementarla, cos come di
seguito riportato:
If txtAppunti.CanUndo Then
txtAppunti.Undo()
End If
Si tratta, infatti, di testare prima con la propriet
CanUndo se possibile annullare qualcosa ed,
in seguito, grazie al metodo Undo(), si procede
allannullamento dellultima operazione effet-
tuata. Se si volesse ripulire il buffer per non con-
sentire di ripristinare quanto appena annullato,
bisogna utilizzare il metodo txtAppun-
ti.ClearUndo().
Enrico Bottari
LAUTORE
Enrico Bottari lavora da 18
anni nel mondo ICT. Ha
avuto esperienze
significative nel campo
dello sviluppo software su
piattaforme Microsoft,
dellamministrazione di
database Oracle e della
gestione di sistemi UNIX
Solaris System V.
Attualmente responsabile
delle attivit operazionali
(LAN, Office Automation e
servizi Desktop) presso la
sede italiana di unimpor -
tante organizzazione
internazionale.
Fig. 2: La gestione dei cambiamenti avviene tramite
unesplicita richiesta di conferma
Fig. 3: La finestra di dialogo per ricercare il testo realiz-
zata tramite la funzione di Visual Basic.NET InputBox()
053-058:072-080 30-09-2008 16:15 Pagina 59
ht t p: / / www. i opr ogr ammo. i t
MOBILE M
G
60
/Novembre 2008
Programmare le interfacce grafiche di basso livello
I
componenti di interfaccia esaminati nel
corso delle precedenti puntate ci torneranno
sicuramente utili. Immaginiamo che, al ter-
mine di una partita, lutente abbia realizzato un
punteggio da record. Il nostro gioco offre la pos-
sibilit di inviare questo punteggio attraverso
Internet, per memorizzarlo in una top ten globa-
le. Lutente dovr necessariamente inserire il
proprio nome. Potremo raccogliere linforma -
zione servendoci di un TextField in un Form, o di
un TextBox direttamente sul display. Non dovre-
mo programmare da zero un meccanismo per
raccogliere linput testuale. I componenti grafici
di alto livello ci torneranno utili proprio per com-
piti di questo tipo. Il gioco vero e proprio, per,
pu servirsi ben poco dei componenti di alto
livello. Per realizzare un gioco necessario con-
trollare pixel per pixel quel che appare sul
display del dispositivo. I componenti di alto livel-
lo non offrono alcuna funzionalit per inserire
nel display astronavi, paesaggi e personaggi da
animare. Per far questo necessario fare grafica
in libert, andando a controllare a un pi basso
livello quel che compare sullo schermo. Questa
lezione, con quelle immediatamente a seguire, vi
introdurranno alle interfacce grafiche di basso
livello, spiegando come fare per ottenere il con-
trollo totale di quello che mostrato sul display.
UTILIZZO
DELLA CLASSE CANVAS
Nel corso della terza parte di questa serie
(ioProgrammo 129) abbiamo spiegato come
linterfaccia Displayable venga implementata, in
primo luogo, dalle due classi Screen e Canvas.
Da Screen derivano tutti i componenti utili per
assemblare interfacce grafiche di alto livello,
come Alert e Form, gi esaminati nel corso degli
appuntamenti pi recenti. La classe Canvas
(javax.microedition.lcdui.Canvas), che ora diviene
oggetto del nostro studio, deve invece essere uti-
lizzata ogni volta che si desidera accedere al con-
trollo delle funzionalit grafiche di basso livello.
Canvas una classe astratta. Le classi astratte
non possono essere istanziate, in genere perch
contengono uno o pi metodi di cui stata forni-
ta la firma ma non limplementazione (un po
come succede con le interfacce). Una classe
astratta pu soltanto essere estesa, e chi la esten-
de deve implementare tutti i metodi che nella
genitrice sono privi di corpo (tranne nel caso in
cui sia astratta anche la classe derivata). Per que-
sto motivo luso che dovremo fare di Canvas
particolare rispetto ai casi precedentemente
analizzati. Da nessuna parte potremo scrivere:
Canvas canvas = new Canvas();
// Errore di compilazione! Canvas astratta!
display.setCurrent(canvas);
Dovremo piuttosto fare:
public class MiaCanvas extends Canvas
{
// Implementazione dei metodi necessari
}
Poi, nella MIDlet, saremo finalmente liberi di
richiamare:
MiaCanvas canvas = new MiaCanvas();
display.setCurrent(canvas);
Scendiamo un po pi nel dettaglio. Canvas ha
un solo metodo astratto, cio privo di implemen-
tazione:
public void paint(Graphics g)
A paint(), di fatto, tocca il lavoro duro: proprio
al suo interno che deve essere stabilito, pixel per
pixel, quel che dovr apparire sul display.
Largomento passato al metodo viene detto con-
testo grafico, e fornisce tutte le funzionalit di cui
abbiamo bisogno per il disegno. Il contesto grafi-
Conoscenze richieste
Rudimenti di Java
Software
JDK, WTK
Impegno
Tempo di realizzazione
REQUISITI
VIDEOGIOCHI IN JAVA
PER CELLULARI
SESTO APPUNTAMENTO. LO SVILUPPO DI UN GIOCO, OLTRE A PREVEDERE LUTILIZZO DI COMPO-
NENTI GRAFICI AD ALTO LIVELLO, SPESSO E VOLENTIERI NECESSITA DI STRUTTURE GRAFICHE
DI BASSO LIVELLO. IN QUESTO APPUNTAMENTO CI OCCUPEREMO PROPRIO DI QUESTE ULTIME
J CD J WEB
videogiochi_per_cellulari_06.rar
cdrom.ioprogrammo.it
060-065:032-035 2-10-2008 10:39 Pagina 60
co fornito al metodo paint() in diretto collega-
mento con quanto mostrato sul display: quel che
disegniamo nel contesto grafico viene trasferito
direttamente nello schermo del dispositivo.
LA CLASSE GRAPHICS
La classe Graphics (javax.microedition.lcdui.Gra -
phics) realizza il contesto grafico necessario per
eseguire le operazioni di disegno. Un contesto gra-
fico indissolubilmente legato ad una matrice di
pixel, cio ad unimmagine. Quando si manipola il
contesto grafico si modifica il contenuto dellim-
magine cui collegato. Graphics offre una serie di
metodi utili per il disegno: linee, rettangoli, curve e
cos via. Inoltre, sempre attraverso il contesto grafi-
co, possibile introdurre nella matrice collegata
delle scritte e delle immagini, sia caricate dal file
system locale sia prelevate attraverso una rete. Non
ci troveremo mai ad istanziare Graphics in maniera
diretta: otterremo sempre oggetti di questo tipo gi
pronti alluso. Due sono i casi in cui li useremo:
quando un contesto grafico ci viene passato auto-
maticamente in un metodo paint(), oppure quan-
do chiederemo esplicitamente di avere il contesto
grafico associato ad unimmagine modificabile.
GESTIONE
DELLE COORDINATE
Per identificare ciascun pixel appartenente a un
contesto grafico necessario ricorrere ad un siste-
ma di riferimento. Graphics ci offre un sistema di
riferimento molto simile al piano cartesiano, che
identifica ciascun pixel attraverso unascissa (coor-
dinata x) ed unordinata (coordinata y). Lorigine
del sistema posta in alto a sinistra. Lasse delle
ascisse cresce da sinistra verso destra, mentre quel-
lo delle ordinate si muove dallalto verso il basso.
Mentre in geometria si assume che il punto non
abbia dimensione, lunit atomica del nostro
sistema di riferimento, il pixel, ha una misura
propria. Tracciamo idealmente delle linee fra i
pixel del contesto grafico. Si ottiene una griglia.
Le coordinate del sistema, piuttosto che identifi-
care i singoli pixel, individuano gli incroci fra le
linee della griglia.
I pixel che hanno coordinata x o y pari a 0 non
giacciono sugli assi, ma sono immediatamente al
di sotto (nel caso della coordinata y) o alla destra
(coordinata x) di essi. In un contesto largo n e
alto m, il pixel pi in alto a sinistra ha coordinate
(0, 0), mentre il pi basso a destra sar alla posi-
zione (n - 1, m - 1).
Questo meccanismo ci permette di identificare
esattamente ogni posizione del contesto grafico.
Potremo quindi comandare disegna una retta che
va dal punto (5, 3) al punto (10, 8), oppure disegna
un rettangolo alle coordinate (6, 2) di larghezza 15 e
ht t p: / / www. i opr ogr ammo. i t
M
MOBILE
Novembre 2008/
61
G
Programmare le interfacce grafiche di basso livello
NOTA
MIDP JAVADOC
Ora che si sta iniziando a
lavorare con le API di Java
ME, in particolare con
quelle di MIDP, un po di
documentazione javadoc
fa sempre comodo. Il
vostro WTK contiene i
javadoc di numerose
librerie collegate alla
piattaforma. Quelli per
MIDP li trovate ad un
percorso del tipo:
<DIRECTORY DEL
WTK>/docs/api/midp
Fig. 1: Nel sistema di riferimento usato da
Graphics, lorigine degli assi in alto a sinistra. Il
valore della coordinata x aumenta andando da sini-
stra verso destra, mentre quello della coordinata y
cresce spostandosi dallalto verso il basso
Fig. 2: La griglia che si ottiene tracciando delle
linee fra i pixel
Fig. 3: Il pixel alle coordinate (0, 0) non coincide
in realt con lorigine, ma immediatamente sotto
e a destra di essa
060-065:032-035 2-10-2008 10:39 Pagina 61
ht t p: / / www. i opr ogr ammo. i t
MOBILE M
G
62
/Novembre 2008
Programmare le interfacce grafiche di basso livello
altezza 10, ma anche stampa una certa scritta a
partire dalle coordinate (3, 5) e incolla questa
immagine a partire dalle coordinate (8, 3).
Se il punto in cui posta lorigine degli assi, per
qualche motivo, vi risulta scomodo, potete sem-
pre traslare lorigine verso unaltra coordinata.
Sono disponibili i seguenti metodi:
G public void translate(int x, int y)
Sposta lorigine degli assi al punto (x, y). Dopo
la chiamata al metodo, quel che nel riferimen-
to precedente era il punto (x, y) sar (0, 0) nel
nuovo sistema.
G public int getTranslateX()
Restituisce la coordinata x dellorigine corren-
te rispetto a quella predefinita in alto a sini-
stra.
G public int getTranslateY()
Restituisce la coordinata y dellorigine corren-
te rispetto a quella predefinita in alto a sini-
stra.
Fate attenzione al fatto che le chiamate a trans-
late() risultato cumulative. Se prima invocate
translate(3, 4) e poi translate(2, 6), lorigine degli
assi, rispetto al sistema di riferimento di parten-
za, si trover al punto (5, 10).
GESTIONE DEL COLORE
Prima di disegnare un punto, una linea, un ret-
tangolo o quel che volete, necessario imposta-
re il colore da utilizzare. Graphics mette a dispo-
sizione due metodi utili allo scopo:
G public void setColor(int red, int green, int blue)
G public void setColor(int rgb)
I colori, in ambito informatico, vengono spesso
definiti come una mescolanza di tre canali: rosso
(red), verde (green) e blu (blue), da cui la sigla
RGB. Quando tutti e tre i canali sono a zero si
ottiene il nero. Quando tutti e tre i canali sono al
loro massimo valore si ottiene il bianco.
Se il canale rosso non nullo, ma lo sono gli altri
due, si ottiene una sfumatura di rosso. Quando
il canale verde lunico a non essere nullo si ottie-
ne una sfumatura di verde. Lo stesso vale per il
canale blu. Tutte le rimanenti combinazioni
intermedie identificano ogni altro colore sup-
portato. Nello standard di MIDP ciascun canale
pu avere valore compreso tra 0 e 255 (estremi
inclusi). Si ottengono pi di sedici milioni di
combinazioni possibili. Il primo metodo fra
quelli presentati permette di impostare singolar-
mente il valore di ciascuno dei tre canali, in
maniera semplice ed intuitiva. Il secondo per-
mette di specificare il colore servendosi di un
solo argomento. Troverete comoda questa forma
se siete gi abituati, da esperienze precedenti, a
lavorare con i colori in notazione esadecimale.
Infatti possibile scrivere:
graphics.setColor(0xFF0000)
per ottenere lo stesso risultato di:
graphics.setColor(255, 0, 0)
Non mi dilungher nellillustrare la definizione
dei colori attraverso la notazione esadecimale.
Se gi la conoscete, buon per voi. In caso contra-
rio non c problema, data la disponibilit di una
forma alternativa che controlla singolarmente i
tre canali RGB.
Se non avete mai lavorato con la definizione
RGB dei colori, la seguente applicazione vi tor-
ner molto utile. Si tratta di unapplicazione Java
Swing per la Standard Edition di Java, che potre-
te compilare ed eseguire sul vostro desktop.
Vi aiuter a capire le corrispondenze fra i valori
dei tre canali ed il colore effettivamente elabora-
to.
package it.ioprogrammo.rgbviewer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class RGBViewer extends JFrame implements
ChangeListener {
private static final long serialVersionUID = 1L;
private JSlider redSlider = new JSlider(0, 255, 0);
private JSlider greenSlider = new JSlider(0, 255, 0);
private JSlider blueSlider = new JSlider(0, 255, 0);
private JPanel colorPanel = new JPanel();
private JLabel colorLabel = new JLabel("0 0
NOTA
QUALE VERSIONE
DI MIDP?
Allindirizzo
http://www.club-
java.com/TastePhone/J2M
E/MIDP_mobile.jsp
troverete uninteressante
lista, in continuo
aggiornamento, che
abbina a ogni modello di
cellulare la versione di
MIDP supportata.
060-065:032-035 2-10-2008 10:39 Pagina 62
ht t p: / / www. i opr ogr ammo. i t
0", JLabel.CENTER);
public RGBViewer()
{
super("RGBViewer");
redSlider.addChangeListener(this);
greenSlider.addChangeListener(this);
blueSlider.addChangeListener(this);
JPanel north = new JPanel(new GridLayout(6, 1));
north.add(new JLabel("Rosso", JLabel.CENTER));
north.add(redSlider);
north.add(new JLabel("Verde", JLabel.CENTER));
north.add(greenSlider);
north.add(new JLabel("Blue", JLabel.CENTER));
north.add(blueSlider);
colorPanel.setPreferredSize(new
Dimension(200, 100));
colorPanel.setBorder(new EtchedBorder());
colorPanel.setBackground(Color.BLACK);
JPanel all = new JPanel(new BorderLayout(3, 3));
all.setBorder(new EmptyBorder(5, 5, 5, 5));
all.add(north, BorderLayout.NORTH);
all.add(colorPanel, BorderLayout.CENTER);
all.add(colorLabel, BorderLayout.SOUTH);
getContentPane().add(all);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void stateChanged(ChangeEvent e) {
int r = redSlider.getValue();
int g = greenSlider.getValue();
int b = blueSlider.getValue();
colorPanel.setBackground(new Color(r, g, b));
colorLabel.setText(r + " " + g + " " + b);
}
public static void main(String[] args)
{
RGBViewer mainFrame = new RGBViewer();
mainFrame.setVisible(true);
}
}
Il colore impostato pu successivamente essere
recuperato servendosi dei metodi:
G public int getRedComponent()
Restituisce il valore del canale rosso.
G public int getGreenComponent()
Restituisce il valore del canale verde.
G public int getBlueComponent()
Restituisce il valore del canale blu.
G public int getColor()
Restituisce la mescolanza corrente espressa in
notazione esadecimale.
Non detto che il dispositivo in uso supporti
tutte le sedici milioni e passa di combinazioni
possibili. In tal caso la preoccupazione resta
comunque marginale: se la mescolanza impo-
stata non fra quelle disponibili, il dispositivo
provveder automaticamente a mettere in uso la
pi simile fra quelle supportate. Ad ogni modo
sempre possibile interrogare il dispositivo per
sapere quale combinazione fatta effettivamen-
te corrispondere ad ogni possibile mescolanza
comandata, attraverso il metodo:
G public int getDisplayColor(int color)
Purtroppo questo metodo, disponibile a partire
da MIDP 2.0, lavora esclusivamente con la nota-
zione esadecimale. Largomento fornito il colo-
re desiderato dal programmatore, mentre il
valore restituito la combinazione che, in caso,
il dispositivo gli farebbe effettivamente corri-
spondere. Se non conoscete la notazione esade-
cimale dei colori, ma avete ugualmente inten-
zione di usufruire del metodo, potrete sfruttare
la seguente classe di utilit nelle vostre MIDlet:
package it.ioprogrammo.util;
public class ColorUtils
{
public static int getHexColor(int red, int
green, int blue)
{
int retValue = 0;
M
MOBILE
Novembre 2008/
63
G
Programmare le interfacce grafiche di basso livello
Fig. 4: Lapplicazione RGBViewer (per il desktop,
non per il cellulare) aiuta a comprendere meglio la
composizione dei colori attraverso la mescolanza
dei tre canali RGB
NOTA
SIGLARIO
Java ME, JME o J2ME
Java Micro Edition
WTK
Java Wireless Toolkit
MIDP
Mobile Information Device
Profile
CLDC
Connected Limited Device
Configuration
060-065:032-035 2-10-2008 10:39 Pagina 63
ht t p: / / www. i opr ogr ammo. i t
retValue = retValue | blue;
retValue = retValue | (green << 8);
retValue = retValue | (red << 16);
return retValue;
}
public static int getRedComponent(int rgb)
{
return (rgb & 0xff0000) >>> 16;
}
public static int getGreenComponent(int rgb)
{
return (rgb & 0xff00) >>> 8;
}
public static int getBlueComponent(int rgb)
{
return rgb & 0xff;
}
}
La classe contiene quattro metodi statici. Date le
tre componenti di un colore possibile ottenere
il corrispondente valore esadecimale invocando:
int color = ColorUtils.getHexColor(rosso, verde, blu);
Al contrario, dato un colore in notazione esadeci-
male, possibile ricavare i valori dei tre distinti
canali usando:
int rosso = ColorUtils.getRedComponent(color);
int verde = ColorUtils.getGreenComponent(color);
int blu = ColorUtils.getBlueComponent(color);
Se il display del dispositivo a toni di grigio, il
colore impostato con uno dei metodi setColor()
viene automaticamente trasformato nella sfu-
matura di grigio che meglio ne riflette la brillan-
tezza. In alternativa, se il display stato a priori
riconosciuto come a toni di grigio, conviene
usare i metodi:
G public void setGrayScale(int value)
Imposta un tono di grigio usando largomento
fornito, che deve essere compreso tra 0 e 255
(estremi inclusi). Il valore 0 corrisponde al
nero, mentre 255 il bianco (dico bianco a
livello logico, visto che di solito negli schermi a
toni di grigio questo valore corrisponde allo
sfondo del display, vale a dire al pixel spento).
G public int getGrayScale()
Restituisce il tono di grigio impostato prece-
dentemente con setGrayScale() oppure elabora-
to automaticamente a partire da un colore
impostato mediante setColor().
Anche nel caso dei dispositivi con display a toni
di grigio non detto che tutte e 256 le combina-
zioni possibili siano supportate.
Automaticamente, ogni sfumatura non visualiz-
zabile sar sostituita dalla pi prossima fra quel-
le supportate. Il metodo getDisplayColor(), anche
in questo caso, fornisce informazioni sulle effet-
tive corrispondenze.
In generale, dunque, possiamo osservare come i
dispositivi con display a colori o a toni di grigio
siano perfettamente in grado di automatizzare
ed ottimizzare la selezione dei colori quando
viene comandato luso di una tinta non riprodu-
cibile. Spesso e volentieri non c neanche biso-
gno di distinguere fra gli schermi a colori e quel-
li a toni di grigio: si pu lavorare come se si stes-
se sempre manipolando un display con sedici
milioni di combinazioni possibili, fidandosi cie-
camente degli automatismi eventualmente
messi in atto dal dispositivo. Lunica accortezza
che vi raccomando di usare lapplicare sempre
un netto contrasto ogni volta che due elementi
grafici vicini devono risultare nettamente distin-
guibili. Ad esempio evitate di mettere una scritta
verde su uno sfondo di un verde poco pi chiaro
o poco pi scuro: se il dispositivo in uso non sup-
porta lintera gamma delle sfumature, potrebbe
accadere che le due tinte vengano entrambe
risolte nella medesima mescolanza, rendendo la
scritta indistinguibile dallo sfondo.
Problemi pi seri emergono manipolando i
dispositivi monocromatici, o in bianco e nero
che dir si voglia (intendendo sempre un bianco
logico). Anche in questo caso avvengono delle
conversioni automatiche, ma i valori di destina-
zione sono soltanto due. Un gioco che sfrutta
svariati milioni di colori pu lo stesso funzionare
con solo qualche migliaio di tinte o anche meno.
Qualche dettaglio visivo andr sicuramente
perso, ma usando le accortezze citate prima, i
livelli di usabilit e di giocabilit ne usciranno
intatti. Lo stesso non si pu dire trasferendo il
gioco su un display monocromatico. Se avete
intenzione di supportare questa categoria di
schermi dovrete di fatto allestire una linea grafi-
ca alternativa, studiata esclusivamente per la
situazione. Il display in uso potr poi essere rico-
nosciuto a tempo di esecuzione, in modo da cari-
care ed eseguire la linea grafica pi consona alla
circostanza.
GESTIONE DEL TRATTO
Oltre al colore, possibile gestire il tratto, cio la
maniera in cui vengono tracciate le linee ed i
MOBILE M
G
64
/Novembre 2008
Programmare le interfacce grafiche di basso livello
060-065:032-035 2-10-2008 10:39 Pagina 64
ht t p: / / www. i opr ogr ammo. i t
bordi. Le possibilit offerte sono due:
G Graphics.SOLID
Identifica una linea di disegno continua.
G Graphics.DOTTED
Identifica una linea di disegno tratteggiata.
Il tratto si controlla attraverso i metodi:
G public void setStrokeStyle(int style)
Imposta il tratto.
G public int getStrokeStyle()
Restituisce il tratto in uso.
DISEGNO DI FORME
GEOMETRICHE
A questo punto non resta che imbrattare la
nostra tela. Cominciamo con le linee. Per dise-
gnare una linea servono due punti, uno di par-
tenza ed uno darrivo. Il metodo utile :
G public void drawLine(int x1, int y1, int x2, int y2)
Disegna una linea che va dal punto (x1, y1) al
punto (x2, y2), usando il colore e il tratto cor-
rente.
Passiamo ai rettangoli. Per disegnare un rettan-
golo sono necessari quattro dati: una coordinata
x, una coordinata y, una misura di larghezza ed
una di altezza. Il sistema comincia a disegnare
dal punto (x, y) specificato, quindi sviluppa il ret-
tangolo verso destra e verso il basso, ponendo
lestremo inferiore al punto di coordinate (x +
larghezza, y + altezza). Ragionando (con lausilio
della Fig. 5) vi renderete conto che il rettangolo, a
lavoro ultimato, si sviluppa per larghezza + 1 pixel
in orizzontale e per altezza + 1 pixel in verticale.
Il sistema pone i due estremi in (2, 2) e in (2 + 6,
2 + 4), cio (8, 6). Contate ora quanti pixel ci sono
lungo i lati del rettangolo: ne misurerete 9 lungo
le x e 5 lungo le y.
I metodi per il disegno dei rettangoli sono:
G public void drawRect(int x, int y, int width, int
height)
Disegna il bordo di un rettangolo con estremo
superiore in (x, y) ed estremo inferiore in (x +
width, y + height), usando il colore ed il tratto
correnti.
G public void drawRoundRect(int x, int y, int
width, int height, int arcWidth, int arcHeight)
Disegna il bordo di un rettangolo con estremo
superiore in (x, y) ed estremo inferiore in (x +
width, y + height), usando il colore ed il tratto
correnti. I quattro angoli del rettangolo vengo-
no arrotondati con un arco di larghezza
arcWidth e altezza arcHeight.
G public void fillRect(int x, int y, int width, int
height)
Come drawRect(), ma il rettangolo in questo
caso viene riempito con il colore corrente.
G public void fillRoundRect(int x, int y, int width,
int height, int arcWidth, int arcHeight)
Come drawRoundRect(), ma il rettangolo in
questo caso viene riempito con il colore cor-
rente.
Altre funzionalit disponibili permettono il
disegno di triangoli e curve.
Giacch poco utili per quelli che sono i nostri
scopi (lavoreremo soprattutto con immagini
preconfezionate), i metodi corrispondenti non
vengono qui descritti. Fate riferimento alla
documentazione ufficiale del linguaggio se
siete interessati ad approfondire queste carat-
teristiche.
Sul CD allegato alla rivista, troverete un esem-
pio riassuntivo che mette in pratica le nozioni
appena apprese.
CONCLUSIONI
La trattazione prosegue sul prossimo numero.
Imbratteremo la nostra tela con testi ed imma-
gini, ed impareremo anche alcune tecniche
indispensabili per la corretta e completa
gestione di una Canvas. Come sempre non
mancheranno le dimostrazioni pratiche. Non
potete perderlo!
Carlo Pelliccia
M
MOBILE
Novembre 2008/
65
G
Programmare le interfacce grafiche di basso livello
Fig. 5: Si comanda il disegno di un rettangolo alle
coordinate (2, 2), con larghezza 6 e altezza 4
LAUTORE
Carlo Pelliccia lavora
presso la Software Factory
di Altran Italia, dove si
occupa di analisi e
sviluppo software per
piattaforme Java. Nella
sua carriera di technical
writer ha pubblicato
cinque manuali ed oltre
centocinquanta articoli,
molti dei quali proprio tra
le pagine di ioProgrammo.
Il suo sito, che ospita
anche diversi progetti Java
Open Source, disponibile
allindirizzo
www.sauronsoftware.it
060-065:032-035 2-10-2008 10:39 Pagina 65
ht t p: / / www. i opr ogr ammo. i t
LIBRARY M
G
70
/Novembre 2008
Familiarizziamo con la libreria Scientific Library (GSL)
C
i sono molti modi di generare sequenze di
numeri casuali, e ogni linguaggio di pro-
grammazione ne offre una o pi implemen-
tazioni, direttamente o tramite librerie. Una delle li-
brerie create per fornire metodi matematici la GNU
Scientific Library (GSL). GSL in grado di generare mol-
to facilmente degli istogrammi. Linstallazione sot-
to Linux non comporta alcun problema: la sequen-
za solita ./configure; make; make install compila ed
installa la libreria. La compilazione sotto Windows
prevede luso di Gcc con Cygwin, lasciando invaria-
ta la sequenza di operazioni. GSL fornisce unampia
selezione di strumenti per chi scrive programmi:
Nel nostro progetto vedremo come usare alcune fun-
zioni relative alla generazione di numeri casuali e al-
la creazione di istogrammi.
LA DISTRIBUZIONE
DI PROBABILIT
Listogramma delle probabilit un grafico bidimen-
sionale. Riporta sullasse delle ascisse (orizzontale) gli
eventi possibili, sullasse delle ordinate le occorrenze
di ogni evento. Lo stile pi diffuso dellistogramma
un diagramma a barre. La distribuzione di probabi-
lit associata a un istogramma consiste in un insieme
di caselle misuranti la probabilit per un evento di ri-
cadere entro un certo range di una variabile continua
x. In pratica, la probabilit che esca 1 y > 0, la pro-
babilit che esca 1 o 2 y > y, e cos via. Alla fine, la pro-
babilit che esca un numero tra 1 e 6 1. Una funzio-
ne di distribuzione di probabilit definita, in GSL,
da una struttura contenente la funzione di distribu-
zione della probabilit cumulata.
PROVIAMO
CON UN DADO
Il problema del lancio di uno o pi dadi archetipi-
co del calcolo delle probabilit, quasi quanto quello
del lancio della moneta. La probabilit di uscita di
un numero, in caso di dado equo, deve essere ugua-
le a quella di uscita di ogni altro numero (gli eventi so-
no equiprobabili): nel caso osservato, 1/6. Per os-
servarne sperimentalmente la veridicit, occorre fa-
re un gran numero di lanci (in teoria, infiniti) e os-
servare la validit generale della legge dei grandi nu-
meri: la frequenza tende alla probabilit. Disegne-
remo quindi lesperimento in modo da calibrare il
numero dei lanci, e ne tracceremo listogramma.
Vediamo alcuni brani di codice:
#define MAX 6
gsl_rng * rand_gen = gsl_rng_alloc (gsl_rng_taus);
gsl_histogram * distr_hist = gsl_histogram_alloc (MAX);
double range[MAX + 1] = { 0.5, 1.5, 2.5, 3.5, 4.5, 5.5,
6.5 };
Abbiamo definito le variabili di tipo numero casua-
le (creando unistanza di generatore di Tausworthe)
e istogramma, allocando loro memoria.
Per listogramma abbiamo anche definito la di-
mensione orizzontale delle barre, dando i limiti inferiore
e superiore di ogni range. Con le seguenti istruzioni
creiamo la struttura contenitore dei dati dellisto-
gramma e inizializziamo il generatore di numeri ca-
suali.
gsl_histogram_set_ranges (distr_hist, range, MAX + 1);
gsl_rng_set (rand_gen, time(NULL));
Infine, allinterno di un ciclo, potremo generare il nu-
mero casuale (effettuare un lancio) e incrementare la
barra relativa dellistogramma:
gsl_histogram_increment (distr_hist,
(double) gsl_rng_uniform_int (rand_gen, MAX) + 1);
Qui la tecnica usata banale: genera un numero, con
distribuzione uniforme, tra 0 e MAX 1, e sommagli
1. Trasferire i dati dellistogramma a stdout facilis-
simo, grazie allistruzione fornita da GSL:
gsl_histogram_fprintf (stdout, distr_hist, "%g", "%g");
Infine, liberare la memoria allocata alle strutture pri-
STUDIAMO IL LANCIO
DEI DADI CON LE GNU
ABBIAMO DIVERSI MODI PER GENERARE NUMERI CASUALI. LA GNU SCIENTIFIC LIBRARY, A
TAL FINE, PROMETTE DI FORNIRE OTTIMI METODI. SFRUTTIAMOLI PER SIMULARE IL LANCIO
DI DADI E VEDERNE GRAFICAMENTE LA DISTRIBUZIONE
J CD J WEB
lancio_di_dadi.zip
cdrom.ioprogrammo.it
Conoscenze richieste
Compilatori C
Software
Utenti Linux: Gcc;
utenti Windows:
Cygwin e Gcc
(dovrebbe essere
presente una versione
precompilata di GSL)
Impegno

Tempo di realizzazione
REQUISITI
070-071:072-080 30-09-2008 16:29 Pagina 70
ht t p: / / www. i opr ogr ammo. i t
M LIBRARY
Novembre 2008/
71
G
Familiarizziamo con la libreria Scientific Library (GSL)
ma definite semplice come fare un free():
gsl_rng_free (rand_gen);
gsl_histogram_free (distr_hist);
Appena abbiamo compilato il programma, ne possiamo
testare i risultati. Simuliamo il lancio di un dado per
10 milioni di volte: ci aspettiamo di vedere la fre-
quenza di uscita di 1 circa pari a quella di uscita di 2,
o degli altri valori. In Fig. 1 vediamo listogramma
determinato dai dati ottenuti: il suo profilo piatto in-
dica la correttezza dellipotesi.
COSA CAMBIA
CON DUE DADI
Per rafforzare la comprensione delle nuove istruzio-
ni introdotte, e vederne altre, realizziamo un pro-
getto simile: lancio di due dadi e studio della distri-
buzione del risultato complessivo. Ci attendiamo un
picco nellistogramma in corrispondenza dei risul-
tati 6, 7 e 8 (ottenibili come 5+1, 4+2, 3+3, e 6+1, 5+2,
4+3, e 6+2, 5+3, 4+4). Gli altri risultati dovrebbero ve-
rificarsi con minor frequenza, con un minimo in cor-
rispondenza di 2 (1+1) e 12 (6+6). Di nuovo, eseguia-
mo lesperimento con un alto numero di lanci, e ve-
diamo listogramma risultante (Fig. 2):
A questo progetto abbiamo aggiunto la capacit di
restituire alcuni dei valori caratteristici dellisto-
gramma. Inoltre, vedremo quanto sia immediato ot-
tenere la funzione di distribuzione di probabilit.
fprintf (stderr, "Valore minimo nei range: %f\n",
gsl_histogram_min_val (distr_hist));
fprintf (stderr, "Valore massimo nei range: %f\n",
gsl_histogram_max_val (distr_hist));
fprintf (stderr, "Valore medio della distribuzione: %f\n",
gsl_histogram_mean (distr_hist));
fprintf (stderr, "Deviazione standard della distribuzione:
%f\n",
gsl_histogram_sigma (distr_hist));
Le precedenti fprintf() stampano, inviando su stan-
dard error, il minimo valore dellistogramma (il pun-
teggio ottenuto meno frequentemente), quello mas-
simo, e media e deviazione standard della distribuzione
di probabilit associata allistogramma.
gsl_histogram * dhcopy;
dhcopy = gsl_histogram_clone (distr_hist);
gsl_histogram_fprintf (stderr, dhcopy, "%g", "%g");
Vediamo inoltre una delle due operazioni di copia a
disposizione: si clona listogramma assegnando il
puntatore ad una variabile istogramma non ancora
allocata. Se avessimo voluto fare una copia fisica
(istruzione con suffisso memcpy), avremmo dovuto
allocare esplicitamente la memoria alla variabile
dhcopy. Manca solo la generazione del grafico della
distribuzione di probabilit:
gsl_histogram_pdf_init (prob_distr, distr_hist);
gsl_histogram_fprintf (stderr, prob_distr, "%g", "%g");
La prima funzione inizializza una funzione di distri-
buzione in base allistogramma fornito. La seconda
viene usata in maniera forzata, perch non sembra es-
serci una funzione che stampi le funzioni di distri-
buzione. In compilazione generer un warning (di-
verso puntatore a tipo in input), ma alla fine la di-
stribuzione viene stampata ugualmente.
Gianluca Pignalberi
Fig. 1: Istogramma della distribuzione del lancio
di 1 dado (10.000.000 lanci)
Fig. 2: Istogramma della distribuzione del lancio
di 2 dadi (10.000.000 lanci)
Fig. 3: Funzione di distribuzione di probabilit dellesperi-
mento lancio di 2 dadi)
NOTA
LIBRI
IRRINUNCIABILI
Un libro notissimo (lo
definirei un must have) tra i
programmatori C impegnati
a realizzare programmi
fortemente orientati al
calcolo numerico
Numerical Recipes in C.
Gli autori spiegano
dettagliatamente molte
tecniche di realizzazione
delle routine proposte,
indicandone anche le
motivazioni tecniche.
William H. Press, Brian P.
Flannery, Saul A. Teukolsky,
William T. Vetterling,
Numerical Recipes in C: The
Art of Scientific Computing,
New York: Cambridge
University Press, 1992
Un altro libro utile, specie
nel nostro caso, The Art Of
Computer Programming di
Donald Knuth. Il terzo
capitolo del secondo
volume, dedicato proprio
ai numeri casuali.
Donald E. Knuth, The Art of
Computer Programming,
volume 2 (Seminumerical
Algorithms), seconda
edizione; Reading,
Massachusetts: Addison-
Wesley Publishing
Company, 1981.
070-071:072-080 30-09-2008 16:29 Pagina 71
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
72
/Novembre 2008
Sensore di movimento a costo zero grazie al WiiMote
C
ome amante della fotografia naturalistica mi
sono imbattuto, ammirando alcuni scatti,
in eventi ripresi a distanza estremamente
ravvicinata con un animale selvatico; la voglia di
emulare questi grandi fotografi del National Geo-
graphics mi ha spinto a creare una mia soluzione
utilizzando il nostro controller della Wii. Questo
piccolo software vi permetter di risolvere in ma-
niera economica questo semplice problema, ma
potrebbe risultare utile per "catturare", fotografi-
camente parlando, una persona che si avvicinasse
ad una certa zona che volete monitorare. Sfrutte-
remo l'SDK di Flex rilasciato da Adobe sotto licen-
za MPL (Mozilla Public License), comunicheremo
con il controller della Wii tramite un semplice ser-
ver che realizzeremo in C#, completeremo il tutto
adoperando Adobe AIR per poter salvare le istan-
tanee sul nostro hard disk.
ACTIONSCRIPT
FLEX ED AIR
L'evoluzione ed il conseguente successo del lin-
guaggio Actionscript sono stati merito della ricet-
tivit dei programmatori Macromedia, prima, e
Adobe, poi, che hanno implementato sempre pi
soluzioni seguendo le richieste degli sviluppatori
presenti nel mondo; si passati da un "lento" lin-
guaggio interpretato nato per mostrare su schermo
animazioni vettoriarli ad uno compilato e object
oriented eseguito in una veloce virtual machine
(ActionScript Virtual Machine, chiamata AVM2) di
dimensioni veramente ridotte. Il passaggio suc-
cessivo stato quello di favorire i programmatori
"puri", tradizionalmente meno indirizzati alle ani-
mazioni, tramite il rilascio del framework FLEX,
incentrato sullo sviluppo di soluzioni che si basa-
no su componenti grafici ed interfacce predefini-
ti (anche se liberamente personalizzabili). Alla fi-
ne giunto AIR (Adobe Integrated Runtime), cross-
platform runtime environment realizzato allo sco-
po di consentire a ogni programmatore di sfrutta-
re le funzionalit principali presenti su un sistema
operativo senza dover risentire delle restrizioni
presenti nel Flash player: tramite AIR ora possi-
bile consentire ad una RIA (Rich Internet Applica-
tion), realizzata in Flash, Flex o solo Actionscript,
di essere eseguita in locale, con pieno accesso ai
contenuti del vostro hard disk, con un minimo sfor-
zo apportato al codice. Altre societ stanno mo-
strando interesse nella direzione intrapresa da Ado-
be, tra cui Mozilla con Prism e Google con il suo
Docs in primis).
COME INTEGRARE
LE VARIE TECNOLOGIE
Entriamo nel dettaglio su come utilizzeremo que-
ste tecnologie: scriveremo il codice in Actionscript
3, con approccio object oriented, utilizzeremo Flex
per velocizzare lo sviluppo di un'interfaccia grafi-
ca minimale, adoperando i componenti forniti dal-
l'SDK; con relativamente poche righe di codice po-
tremo realizzare la logica del nostro applicativo.
AIR ci consentir di salvare in locale le immagini cat-
turate dalla nostra trappola fotografica senza ob-
bligarci ad inviare tali informazioni ad un server, lo-
cale o remoto che sia; grazie a questa nuova solu-
zione Adobe, potremo anche realizzare un file au-
toinstallante che, opportuanemente firmato digi-
talmente, potrebbe essere distribuito ad altre per-
sone.
MOVIMENTI SOTTO
CONTROLLO COL WII
UN NUOVO ARTICOLO PER ESAMINARE LE POTENZIALIT DEL CONTROLLER WIIMOTE.
VEDREMO COME ADOPERARE LO STESSO PER REALIZZARE, IN FLEX E CON LAUSILIO DI C#,
UN SISTEMA PER SCATTARE INSTANTANEE NEL CASO IN CUI SI RILEVI UN MOVIMENTO
J CD J WEB
wiiphototrap.zip
cdrom.ioprogrammo.it
Conoscenze richieste
OOP, C#, Actionscript 3
Software
C# Express, Flex SDK,
Adobe Flex,
FlashDevelop o altri
Impegno

Tempo di realizzazione
REQUISITI
Fig. 1: L'applicativo che andremo a realizzare
072-075:072-080 30-09-2008 16:49 Pagina 72
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
73
G
Sensore di movimento a costo zero grazie al WiiMote
Poich faremo utilizzo, anche questa volta delle li-
brerie di Brian Peek, scriveremo un server di po-
che righe in C# con un'interfaccia grafica mini-
male in Windows Forms e invieremo un segnale
tramite socket TCP (in Actionscript UDP non ri-
sulta essere disponibile) quando un oggetto inter-
romper la visione del segnale ad infrarosso; tale mes-
saggio sar ricevuto dal nostro applicativo Flex/Air
e dar inizio alla raffica di scatti.
SVILUPPIAMO IL SERVER
IN LINGUAGGIO C#
Il funzionamento di tale applicativo molto sem-
plice: avviando l'eseguibile, invieremo un ping tra-
mite socket TCP (un semplice byte di valore uni-
tario) ad ogni interruzione di segnale infrarosso;
al termine dell'operazione porremo in pausa il ser-
ver per non inviare ulteriori messaggi per un cer-
to intervallo di tempo: lo stato dei segnali ad in-
frarosso che arrivano dal wiimote non risulta ave-
re continuit temporale, ma una rapida sequen-
za che genererebbe una raffica di messaggi per lo
stesso evento.
Poich la fase di attesa del client stata realizzata
in questo caso in maniera sincrona, tramite il me-
todo AcceptTcpClient, per rendere pi compren-
sibile il codice, si avrebbe un freeze dell'intera in-
terfaccia grafica: avviando un semplice thread con-
tenente tale codice si risolve l'inconveniente; tale
metodo termina quando viene instaurata la con-
nessione TCP ed esegue il codice responsabile del-
l'inizializzazione dell'istanza della classe Wiimo-
te permettendoci quindi di monitorare lo stato de-
gli infrarossi:
private void ServerHandler()
{
//Loopback = 127.0.0.1
listener = new TcpListener
(System.Net.IPAddress.Loopback, LISTENPORT);
listener.Start();
logMessage("Listening...");
client = listener.AcceptTcpClient();
logMessage("Client Connected: " +
client.Client.RemoteEndPoint);
logMessage("Getting Stream...");
clientStream = client.GetStream();
logMessage("Starting Wiimote...");
//Inizializziamo il controller e avviamo la
cattura degli eventi del wiimote
SetupWiimote();
}
Il metodo SetupWiimote() lo stesso che stato
utilizzato negli articoli precedenti (ovviamente uti-
Fig. 2: Semplificazione delle tecnologie utilizzate
Fig. 3: Schema che mostra come comunicheranno
gli applicativi
Fig. 4: L'interfaccia grafica del server
NOTA
DRIVER WIIMOTE
E BSOD
Purtroppo anche i driver di
Brian Peek si sono rivelati
per alcuni utenti fonte di
problemi, il famigerato Blue
Screen of Death non si
esentato a presentarsi: la
causa, da quanto ho
appurato, dovuta alla
presenza del software
Bluesoleil; una sua
disinstallazione ha interrotto
immediatamente la
comparsa di tale
schermata.
072-075:072-080 30-09-2008 16:49 Pagina 73
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
74
/Novembre 2008
Sensore di movimento a costo zero grazie al WiiMote
lizzeremo SetReportType(InputReport.IRAccel,true)
per abilitare la ricezione delle sorgenti ad infra-
rosso) quindi viene omesso; la parte di codice pi
interessante quella che gestisce gli eventi rice-
vuti dal wiimote:
private void wm_WiimoteChanged(object sender,
WiimoteChangedEventArgs e)
{
if (
(!e.WiimoteState.IRState.IRSensors[0].Found) &&
(!e.WiimoteState.IRState.IRSensors[1].Found) &&
(!e.WiimoteState.IRState.IRSensors[2].Found) &&
(!e.WiimoteState.IRState.IRSensors[3].Found)
)
{
//Inviamo un messaggio per indicare che qualcosa si
interposto tra controller e sensor bar
if (started)
{
if (clientStream != null)
{
logMessage("Sending Alert to client.");
//Inviamo il nostro ping
clientStream.WriteByte(1);
logMessage("Sleeping...");
//Evitiamo che il server invii un burst di messaggi per
lo stesso evento
Thread.Sleep(5000);
logMessage("Awaken!");
}}}}
Invece di interrompere momentaneamente il thread,
si potrebbe utilizzare un contatore che, ad ogni
certo numero di campioni, invierebbe il messaggio.
Un'ultima nota, ma sul metodo logMessage: prov-
vede a gestire l'inserimento dei messaggi di log ad
un'area di testo creando un delegate se necessa-
rio; questa scelta obbligatoria perch altrimenti
si riceverebbe un messaggio di errore che notifica
l'impossibilt da parte di un thread, che non ri-
sulta essere quello che ha creato il componente (
infatti quello generato della classe Wiimote), di
modificare il suo stato.
L'APPLICATIVO
IN FLEX/AIR
Il software in question contiene un'area centrale
in cui viene mostrata la webcam, sulla destra c'
un TileListDisplay che conterr le anteprime de-
gli scatti effettuati, ed in basso una serie di tab che
mostrano lo stato della connessione con il server,
una finestra di log e una serie di opzioni modifi-
cabili a piacimento (risoluzione della webcam,
scatti al secondo, framerate etc). Si e sfruttata la
possibilit di impostare la sensibilit della web-
cam per avviare la cattura utilizzando il metodo
setMotionLevel, in tal modo si incrementa la pos-
sibilt di intercettare un oggetto in movimento, nel
caso avesse evitato di far scattare l'allarme inter-
rompendo la visibilt delle sorgenti ad infrarosso.
Il metodo captureCameraImage quello che con-
tiene il codice responsabile della creazione dell'i-
stantanea, della sua anteprima, del salvataggio del-
l'immagine e dell'aggiunta di questi dati (rag-
gruppati in un oggetto) alla struttura dati utilizza-
ta dal TileList per mostrare l'anteprima e la data
dell'evento, (un array contenuto in un wrapper
ArrayCollection). La presenza di numeri in esade-
ciamale con otto (ARGB) cifre invece delle classiche
sei (RGB) dovuta all'utilizzo della notazione che
include un valore di trasparenza (la A appunto
Alpha) nelle prime due posizioni.
private function captureCameraImage():void {
//Creiamo l'istanza solo se necessario
if (bitmapData==null) {
bitmapData = new
BitmapData(vid.width,vid.height,false,0x00000000);
}
else if ((bitmapData.width!=vid.width)
||(bitmapData.height!=vid.height)) {
bitmapData = new
BitmapData(vid.width,vid.height,false,0x00000000);
}
//Memorizziamo la fotografia utilizzando il contenuto
del videoDisplay
bitmapData.draw(vid);
//Creiamo l'anteprima da posizionare nella colonna di
destra
//Aggiorniamo l'orario in cui stato scattata la foto
currentDate = new Date().toLocaleTimeString();
//Creiamo una sovraimpressione contenente tale
orario e la disegnamo sul bitmapData
txtField.text=currentDate;
bitmapData.draw(txtField);
//Creiamo l'immagine con un Bitmap utilizzando tale
bitmapData
imgBig = new Image();
imgBig.width = bitmapData.width;
imgBig.height = bitmapData.height;
imgBig.source = new Bitmap(bitmapData.clone());
//Impostiamo i valori di scala da utilizzare, le
proporzioni sono ottenute analizzando il rapporto tra
larghezza in pixel dell'anteprima e la
NOTA
RIFERIMENTI WEB
G http://www.wiili.org
(specifiche, driver e risorse
sul wiimote)
G http://wiibrew.org
(specifiche, driver e risorse
sul wiimote)
G http://www.microsoft.
com/express/vcsharp/
(Visual C# Express Edition
2008)
G http://www.codeplex.
com/WiimoteLib
(la libreria realizzata da
Brian Peek)
072-075:072-080 30-09-2008 16:49 Pagina 74
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
75
G
Sensore di movimento a costo zero grazie al WiiMote
dimensione originale dell'immagine ottenuta dalla
camera; il rapporto di scala dell'altezza relazionato
ovviamente a quello relativo alla larghezza.
mtx = new Matrix(DEFAULT_WIDTH_PREVIEW /
bitmapData.width,0,0,(DEFAULT_WIDTH_PREVIEW/(vi
d.width/vid.height))/bitmapData.height,0,0);
//Creiamo l'anteprima
previewBitmapData = new
BitmapData(DEFAULT_WIDTH_PREVIEW,DEFAULT_WI
DTH_PREVIEW/(vid.width/vid.height));
//Applichiamo la matrice di trasformazione per ridurre
le dimensioni dell'immagine
previewBitmapData.draw(bitmapData.clone(),mtx);
//Creiamo l'immagine di anteprima con un Bitmap
creato utilizzando tale bitmapData
imgPreview = new Image();
imgPreview.width = previewBitmapData.width;
imgPreview.height = previewBitmapData.height;
imgPreview.source = new
Bitmap(previewBitmapData);
Ora utilizziamo le funzionalit di AIR: andremo a
creare un file e salveremo l'immagine su hard disk;
abbiamo prefrito utilizzare il salvataggio sincrono
per non complicare ulteriomente il codice:
//Inizializziamo il file assegnandogli un nome
"univoco",
//La cartella C:\Users\NomeUtente\AppData
\Roaming\WiiPhotoTrap\Local Store.
fileLocation = File.applicationStorageDirectory.
resolvePath((new Date).getTime()+".jpg");
//Apriamo in scrittura il file appena creato
fileStream.open( fileLocation, FileMode.WRITE );
//Codifichiamo utilizzando il formato jpeg l'immagine
e salviamo il file su HD
fileStream.writeBytes(encoder.encode( bitmapData));
//Terminiamo le operazioni sull file collegato allo
stream
fileStream.close();
Provvediamo, infine, a memorizzare un oggetto
creato appositamente per contenere le varie infor-
mazioni, e lo inseriamo nell'array:
//Aggiungiamo data, anteprima, immagine e numero
di scattto all'elenco
TileListdp.addItem({time:currentDate,
image:imgPreview, bigImage:imgBig,
number:imageCounter});
//Incrementiamo il numero di sequenza di scatto
imageCounter++;
}
Per gestire la connessione con il server ci avvan-
taggiamo di un componente scritto ad hoc, chiamato
CustomSocket; questa classe in realt un UICom-
ponent, un componente grafico quindi, nel quale
si inserito sia un elemento grafico (la finestra di
log chiamata TCP-Monitor) che il socket respon-
sabile della gestione della connessione:
public function connect():void {
//Creiamo il socket e lo poniamo in attesa di ricevere
una connessione (evento segnalato dall' Event
SOCKET_DATA)
this.socket = new Socket(host, port);
//Intercettiamo tutti gli eventi che ci possono
interessare che provengono dal socket
this.socket.addEventListener(Event.CLOSE,
closeHandler);
this.socket.addEventListener(Event.CONNECT,
connectHandler);
this.socket.addEventListener(IOErrorEvent.IO_ERROR
, ioErrorHandler);
this.socket.addEventListener(SecurityErrorEvent.SEC
URITY_ERROR, securityErrorHandler);
this.socket.addEventListener(ProgressEvent.SOCKET_
DATA, onData);
}
//Questo metodo provvede a monitorare lo stato del
socket in attesa della ricezione di un messaggio
private function onData( event:ProgressEvent ): void
{
if ( socket.bytesAvailable > 0 )
{
writeLog("Received:"+socket.readByte());
//Notifichiamo l'applicativo che arrivato un
messaggio
dispatchEvent(new Event("ping"));
}
}
CONCLUSIONI
Quando compilerete il codice in questione sar
necessario esportarlo come eseguibile AIR e fir-
marlo digitalmente utilizzando gli applicativi ri-
chiamabili via console, oppure in Flex utilizzando
il comando Export Release Build, in caso contra-
rio potrete utilizzarlo solo all'interno di
Flex/FlashDevelop. In questo applicativo abbia-
mo integrato svariate tecnologie attualmente pre-
senti sul mercato, mostrando con quale facilit pos-
sibile realizzare una soluzione multimediale.
Al prossimo articolo.
Andrea Leganza
LAUTORE
Laureato in Ingegneria
Informatica presso
"La Sapienza - Universit
di Roma". Certificato
EUCIP Core, ha svolto
incarico di tester di
dispositivi mobili per un
grande operatore mobile;
appassionato di fotografia,
lingua giapponese e
istruttore di nuoto FIN,
contattabile presso
neogene@tin.it
o direttamente sul sito
www.leganza.it
072-075:072-080 30-09-2008 16:49 Pagina 75
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
76
/Novembre 2008
Python 3000, tante novit e incompatibilit con il vecchio
Q
uando leggerete queste righe, dovrebbe gi
essere disponibile Python 3000 (Python 3.0
o Py3k). Si tratta di un evento di impor-
tanza epocale per il mondo Python perch, per la
prima volta nella storia di questo linguaggio, si per-
der la compatibilit con le versioni precedenti e i
programmatori saranno costretti a modificare gran
parte del codice esistente. Per quanto possa sem-
brare strano, questa release non introduce nessu-
na funzionalit realmente nuova. Si limita a cor-
reggere molti piccoli errori di progettazione che
affliggevano Python sin dalle prime release. Il team
di sviluppo, infatti, ha deciso di concentrare in que-
sta release tutte le modifiche che imponevano la
rottura della compatibilit con le versioni prece-
denti in modo da porre delle solide basi per le fu-
ture versioni. Questa dovrebbe essere l'ultima vol-
ta che si verifica un cambiamento cos drastico.
In questo articolo esaminiamo le principali novit
di Py3k e spieghiamo come eseguire la migrazione
del codice esistente. A fianco di ogni nuova featu-
re, indichiamo la PEP(Python Enhancement Proposal)
da cui trae origine in modo che possiate approfondire
l'argomento sul web. Le PEP sono i documenti
HTML che descrivono in dettaglio ogni nuova fea-
ture e le motivazioni che hanno portato al suo svi-
luppo. Le trovate allindirizzo http://www.python.
org/dev/peps
Nel corso dell'articolo mostreremo alcuni snippet
di codice. Potete vedere com' fatto un vero pro-
gramma Python 3.0, dando un'occhiata alla im-
plementazione di prova della PEP 362 (Function
Signature Object) creata da Brett Cannon
(http://sayspy.blogspot.com). Tra l'altro, questo uno
dei rari programmi che funzionano sia con Python
2.6 che 3.0. Potete trovare il codice qui:
http://svn.python.org/view/sandbox/trunk/ pep362 .
NIENTE PAURA
Il ramo di sviluppo 2.X verr portato avanti ancora
per diversi anni e continuer a essere pienamente
supportato. La versione 2.7 gi in fase di proget-
tazione ed probabile che verr realizzata anche
una versione 2.8. Il team di sviluppo ha gi prepa-
rato un apposito strumento di traduzione auto-
matica (chiamato 2to3) che in grado di conver-
tire gran parte del codice esistente. Infine, la ver-
sione 2.6 di Python dispone di una opzione di ese-
cuzione (python -3) che costringe l'interprete a mo-
strare dei warning relativi alle funzionalit che non
saranno pi supportate da py3k. Parleremo in det-
taglio della migrazione al termine dell'articolo.
LA STANDARD LIBRARY
L'elemento che ha subito l'intervento pi esteso e
pi visibile stata la libreria standard del linguag-
gio. Oltre 100 moduli sono stati rimossi e 36 sono
stati aggregati tra loro in 6 package di maggiori di-
mensioni. La PEP 3108 documenta nei dettagli le mo-
difiche apportate e ne spiega le ragioni. Nella Tabella
1 riportiamo i casi pi rilevanti. La rimozione di
molti vecchi moduli costringer i programmatori
a intervenire manualmente in molti punti dei loro
programmi. Gli strumenti di conversione auto-
matica del codice non sono in grado di interveni-
re su questo punto.
PYTHON 3000
TUTTE LE NOVIT
LA NUOVA VERSIONE ROMPE LA COMPATIBILIT CON LE VECCHIE E LO FA PER UNA BUONA
CAUSA. TRA LE NUOVE CARATTERISTICHE, UNA MAGGIORE COERENZA INTERNA
E MAGGIORE ELEGANZA DEL CODICE
J CD J WEB
Python3000.rar
cdrom.ioprogrammo.it
Conoscenze richieste
Limitata conoscenza
di Python 2.X
Software
Python3000.
L'interprete gira su
Windows, Linux, BSD,
Mac OS X e molti altri
sistemi operativi.
Impegno

Tempo di realizzazione
REQUISITI
Fig. 1: David Ascher il managing Director di ActiveState
e co-autore, insieme a Mark Lutz, di Learning Python
076-080:072-080 30-09-2008 16:19 Pagina 76
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
77
G
Python 3000, tante novit e incompatibilit con il vecchio
UNICODE,
STRINGS E BYTES
Come previsto dalle PEP 3120 e 3131, Py3k usa Uni-
code (UTF-8, di default) per le stringhe e per gli
identificatori (nomi di variabili, di funzioni e di
classi). Questo vuol dire che si possono avere no-
mi di variabili e di funzioni in ideogrammi cinesi o
commenti in lingua thailandese. Questa funzio-
nalit stata voluta per facilitare l'uso di Py3k co-
me strumento didattico nelle scuole non europee.
La comunit che sviluppa Python, tuttavia, conti-
nuer a usare solo l'inglese ed un set di caratteri
equivalente all'ASCII 7bit. Questo vale soprattutto
per il codice della Standard Library.
In Py3k, stringhe e byte sono oggetti diversi e non
intercambiabili (PEP 3137 e 3112). Le stringhe (str)
sono sempre sequenze di caratteri Unicode e i by-
te (bytes) sono sempre e solo sequenze di byte. Per
convertire questi tipi di dato dall'uno all'altro so-
no disponibili un metodo bytes.decode() che resti-
tuisce la stringa equivalente a una sequenza di by-
te ed un metodo str.encode() che restituisce la se-
quenza di byte equivalente ad una stringa.
PRINT COME FUNZIONE
Lo statement print diventato una funzione (PEP
3105). Questa modifica stata introdotta per por-
re fine agli equivoci e ai bug dovuti alla gestione
degli spazi (soft space) del vecchio comando.
Ora la sintassi la seguente.
print([object, ...][, sep=' '][, end='n'][,
file=sys.stdout])
In cui object l'oggetto (o gli oggetti) da stampare,
sep il separatore da inserire tra i vari elementi da
stampare, end il carattere finale della stringa e
file il nome del file su cui scrivere. Per esempio:
print('hello', 'world', sep=' ', end='\n',
file=output.txt)
Stampa la frase hello world, seguita da un carat-
tere di newline (\n) sul file output.txt.
In modo analogo, anche lo statement exec di-
ventato una funzione. In entrambi i casi, il tool 2to3
in grado di tradurre il codice in modo automati-
co nella stragrande maggioranza dei casi.
CLASSI E METACLASSI
Il vecchio meccanismo di creazione delle classi sta-
to abbandonato e la gerarchia dei tipi stata rior-
ganizzata. Ora tutte le classi derivano esplicita-
mente da object:
class C: # vecchio stile di definizione
(fino Rel. 2.9)
class C(object): # nuovo stile di definizione (Rel.
2.2 -> 2.9)
Si tratta di una finezza di cui pochi dovranno preoc-
cuparsi. Come previsto dalla PEP 3115, cambia-
to anche il meccanismo di dichiarazione di una
metaclasse:
class MyClass(baseclass1, baseclass2,
metaclass=mymetaclass):
anche possibile passare alla classe altri parame-
tri che verranno passati alla metaclasse:
class MyClass(baseclass1, baseclass2,
metaclass=mymetaclass, private=True):
Le metaclassi permettono di controllare alcuni det-
tagli cruciali del meccanismo di creazione delle
classi. David Mertz ha pubblicato su IBM develo-
perworks una trattazione approfondita di questo ar-
gomento:
http://www.ibm.com/developerworks/linux/library/l-
pymeta.html
http://www.ibm.com/developerworks/linux/library/l-
pymeta2/index.html
http://www.ibm.com/developerworks/linux/library/l-
pymeta3.html
ABSTRACT
BASE CLASSES
La PEP 3119 introduce il concetto di Abstract Base
Class (ABC). Una ABC una classe in cui almeno
Fig. 2: Alex Martelli l'autore di Python Cookbook e
Python in a nutshell
NOTA
GUIDO
VON ROSSUM
Guido von Rossum il
padre di Python. Guido un
matematico olandese e
lavora su questo progetto
dal 1991. Dal 2005 lavora
per Google. Nel corso degli
anni, riuscito a raccogliere
attorno a s uno dei team di
sviluppo Open Source pi
attivi e pi qualificati del
mondo.
076-080:072-080 30-09-2008 16:19 Pagina 77
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
78
/Novembre 2008
Python 3000, tante novit e incompatibilit con il vecchio
uno dei metodi non ha implementazione e non
pu essere invocato. La classe astratta viene inse-
rita nella genealogia di un'altra classe per segnalare
al codice esterno la disponibilit dei metodi che
essa definisce (e che la classe derivata implemen-
ta al suo interno). Le ABC svolgono quindi la stes-
sa funzione delle interfacce o delle funzioni ge-
neriche di altri linguaggi e ne ricordano da vici-
no la struttura.
Si possono usare le ABC per fare in modo che una
classe si presenti al mondo esterno come una clas-
se derivata da un'altra classe senza per che esi-
sta una derivazione reale. Per esempio:
class Duck(metaclass=ABCMeta):
@classmethod
def __instancecheck__(cls, x):
# Redefines instancecheck so that it returns true...
return hasattr(x, "__quack__")
# ... if the derived class has a quack attribute.
class AwkwardBird:
def __quack__(self):
return "DoesNotMatter"
isinstance(AwkwardBird(), Duck) # Returns True
In questo caso, la classe AwkwardBird risulta ap-
partenere alla classe Duck, anche se in realt non
deriva da essa. Questo avviene perch il metodo
__instancecheck__ della classe Duck stato ridefi-
nito in modo tale che ritorni True se la classe con
cui viene confrontata dispone di un attributo
__quack__ La PEP 3129 estende l'uso dei decoratori
alle classi, una possibilit che viene poi usata in-
ternamente dal sistema delle ABC. La sintassi la
stessa usata per le funzioni:
@decorator1
@decorator2
class A:
pass # internal class code...
La PEP 3141utilizza le ABC per creare una gerarchia
di tipi di dato che rappresenta varie tipologie di
numeri.
GESTIONE
DELLE ECCEZIONI
Ora tutte le eccezioni devono derivare da BaseEx-
ception(PEP 352) e possono avere un attributo tra-
ceback.
raise RuntimeError("foo occurred").with_traceback
(tracebackobj) # defines an Exceptions with TB
La sintassi di attivazione e di intercettazione delle
eccezioni stata leggermente modificata (PEP 3109,
3110 3134).
try:
print(1 / 0)
except:
raise RuntimeError("Something bad happened")
VISTE SUI DIZIONARI
La PEP 3106 ha introdotto il concetto di dictio-
nary view. Ora i metodi .keys(), .values() e .items()
dell'oggetto dict (l'oggetto dizionario presente
nella Standard Library) restituiscono un set (un
insieme) non ordinato, non pi una lista (list).
Questa modifica permette di operare pi libera-
mente su questi oggetti e risulta di uso pi intuiti-
vo. Ad esempio, possibile verificare se due dizio-
nari contengono le stesse chiavi con un semplice
confronto diretto:
if a.keys() == b.keys():
NUMERI:COSA CAMBIA
La PEP 237 prevede che esista un solo tipo nume-
rico, equivalente al vecchio long ma chiamato int.
In Py3k il risultato di una divisione tra interi sem-
pre un float (numero in virgola mobile), come pre-
visto dalla PEP 238. Questo comportamento sta-
to ritenuto pi intuitivo. La divisione tra interi che
restituisce un intero approssimato per difetto (floor
division) si esegue con il simbolo // (doppio sla-
sh). Ovvero:
NOTA
PYTHON
Python un linguaggio di
programmazione
interpretato, simile a Java
e Ruby. Ha ormai sostituito
il pi anziano Perl in
moltissime applicazioni e
ora considerato uno dei
linguaggi pi diffusi al
mondo. Tra l'altro, viene
usato come linguaggio
interno di automazione e
di personalizzazione da
molti applicativi, come il
desktop manager KDE ed
il modellatore 3D Blender.
Fig. 3: Python Cookbook ActiveState mantiene in funzione da anni questo sito che rac-
coglie circa 2500 'ricette' di programmazione in Python
076-080:072-080 30-09-2008 16:19 Pagina 78
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
79
G
Python 3000, tante novit e incompatibilit con il vecchio
3 / 2 # returns 1,5
3 // 2 # returns 1
FORMATTAZIONE
DI STRINGHE
Seguendo la PEP 3101, stato sostituito il vecchio
meccanismo di formattazione delle stringhe di te-
sto con uno molto pi potente e versatile. Il nuovo
metodo non fa pi uso dell'operatore % ereditato
dal C ed usa invece un nuovo metodo della classe
string: str.format().
"See {0}, {1} and {foo}".format("A", "B", foo="C")
# Returns: "See A, B and C"
Questo nuovo metodo permette di fare riferimen-
to agli elementi variabili del template di testo
usando sia degli identificatori posizionali (numeri)
che dei nomi, anche mescolati tra loro. Inoltre,
possibile fare in modo che il testo venga sostituito
in modo condizionale, sulla base di una variabile
esterna.
OPERATORI
PER LE COMPARAZIONI
Gli operatori di confronto di default di Python han-
no sempre avuto la curiosa capacit di confronta-
re tra loro oggetti di tipo diverso, restituendo ri-
sultati assolutamente privi di senso. Ora questa
funzionalit stata rimossa e Py3k restituisce un er-
rore se si tenta di confrontare oggetti per i quali
non sono stati definiti a priori degli operatori di
confronto adeguati.
Object1 == Object2
# Confronto limitato all'identit. Funziona ancora.
Object1 != Object2
# Confronto limitato alla non
identit. Funziona ancora.
Object1 < Object2
# Solleva una eccezione:
operatore di confronto non definito.
Object1 > Object2
# Solleva una eccezione:
operatore di confronto non definito.
Con l'adozione della PEP 3116, stato completamente
riscritto il sistema di I/O e quindi cambiata la sin-
tassi della funzione open(). La nuova implemen-
tazione aggiunge diverse funzionalit importanti,
tra cui la possibilit di avere o meno un buffer, la pos-
sibilit di decidere un encoding (Unicode) ed il
mapping dei CRLF (Windows) con i LF (Unix). No-
nostante questo, la nuova versione compatibile
quasi al 100% con la vecchia.
open(filename)
# Restituisce un file di testo con buffer
read()
# In questo caso restituisce una stringa
readline()
# In questo caso restituisce una stringa
open(filename, "b")
# Restituisce un file binario con buffer
read()
# Restituisce dei byte quando usata su file binari
readline()
# Non pu essere usata su file binari
NONLOCAL NAME
La PEP 3104 ha introdotto il concetto di nonlo-
cal name. Fino ad ora, Python poteva accedere
solo a oggetti che erano definiti nello spazio dei
nomi locale (la funzione o la classe di apparte-
nenza) o nello spazio globale (il file che contie-
ne lo script od il modulo). Con la dichiarazione
nonlocal ora possibile accedere ad una fun-
zione o ad una variabile che definita nello spa-
zio dei nomi (scope) immediatamente prece-
dente (esterno) a quello in cui ci si trova. Si trat-
ta di una funzionalit simile (ma non equiva-
lente) alle closure di altri linguaggi.
def outer():
x = 42
def inner():
nonlocal x # <--- Accede alla variabile
dichiarata nello scope precedente
Fig. 3: L'indice delle PEP Le Python Enhancement Proposal (PEP) sono i documenti
che descrivono, una per una, le nuove funzionalit e le ragioni che hanno portato alla loro
introduzione
076-080:072-080 30-09-2008 16:19 Pagina 79
ht t p: / / www. i opr ogr ammo. i t
print(x)
x += 1
return inner
IMPORTARE OGGETTI
DA PACKAGE ESTERNI
Py3k segue una logica diversa da Python 2.X per
importare oggetti da package esterni. La nota-
zione standard effettua ora l'importazione solo
da package che sono reperibili in sys.path (solo
nei package standard di sistema). Non cerca pi
l'oggetto all'interno dei package che si trovano nel
path locale. Questo evita collisioni e confusioni
tra package di sistema e package installati dal-
l'utente.
import modulo
# cerca il modulo solo in sys.path
from . import modulo
# cerca anche nel percorso locale
nome-completo-package import modulo
# carica il modulo direttamente
LA MIGRAZIONE
Come abbiamo detto, esiste gi uno strumento au-
tomatico (incluso nella distribuzione ) per la con-
versione del codice da Python 2.X a Py3k: 2to3. Ov-
viamente, questo tool non in grado di sostitui-
re gli import di moduli non pi esistenti e non
in grado di intervenire l dove necessaria una
scelta da parte del programmatore. Di conse-
guenza, 2to3 in grado di convertire solo una
parte del codice. Il resto deve essere convertito a
mano. Il seguente comando converte nomefile.py
da python 2.X a 3.0 (e conserva una copia del-
l'originale).
2to3 -w nomefile.py
Purtroppo, in questo momento 2to3 ancora af-
flitto da alcuni brutti bug, tra cui almeno uno
considerato bloccante (http://bugs.python.org/ is-
sue3131), e quindi non pu essere sfruttato ap-
pieno. Va per detto che gi allo studio una
nuova versione di 2to3, sviluppata durante il Goo-
gle Summer of Code 2008: http://isnomore.net/2to3
/gsocapplicationrev4.
Sul web sono disponibili degli articoli molto uti-
li a comprendere meglio il meccanismo del por-
ting con 2to3. Ad esempio, Sam Ruby, l'autore di
Universal Feedparser (http://www.feedparser.org)
ha fatto un primo, positivo esperimento con 2to3
e ha descritto la sua esperienza sul suo blog
(http://www.intertwingly.net/blog/2007/09/01/2to3),
mettendo a disposizione sia i log che i file diff. Il
team di Django ha fatto una esperienza di mi-
grazione molto significativa ed ha pubblicato un
report molto dettagliato a questo indirizzo:
http://wiki.python.org/moin/PortingDjangoTo3k .
Python 2.6 supporter la migrazione a Py3k in
due modi. Il primo consiste in una nuova op-
zione di invocazione dell'interprete (python -3)
che costringe l'interprete Python a visualizzare
una serie di avvertimenti riguardo alle funzio-
nalit che non saranno pi supportate in Py3k. Il
secondo consiste nella importazione del modu-
lo __future__ . Questo modulo contiene (o, me-
glio, conterr) i backport di molte (ma non tut-
te) le funzionalit di Py3k in Python 2.6. Il se-
guente comando importa la funzionalit Fea-
tureName dal modulo __future__.
from __future__ import FeatureName
Giovanni Bajo, nella sua presentazione di Py3k a
PyCon1 (Giugno 2007) ha proposto un percorso di
migrazione che ci sentiamo di consigliare a tutti:
1) Aggiungere ed utilizzare tutti i __future__ di-
sponibili, in modo da adattare il codice a Py3k in
modo progressivo.
2) Eseguire la propria test suite con python -3
3) Convertire il codice con 2to3
4) Ripulire a mano il codice cos ottenuto
Potete trovare le slide di Bajo qui:
http://www.pycon.it/static/talks/py3k-pycon.pdf . Il video
del suo talk disponibile sul sito di Develer
(http://www.develer.com).
Alessandro Bottoni
SISTEMA M
G
80
/Novembre 2008
Python 3000, tante novit e incompatibilit con il vecchio
NOTA
MULTIPARADIGMA
Python permette di
scrivere codice secondo
diverse modalit, dalla
programmazione
imperativa (come C), alla
programmazione ad
oggetti (come C++) alla
programmazione
funzionale (come Lisp,
Scheme ed Erlang).
Dispone anche di alcuni
costrutti utili al design-by-
contract (a l Eiffel).
Fig. 2: Giovanni Bajo, di Develer SRL, uno dei pi noti
esperti italiani di Python. Sono suoi alcuni dei pi interes-
santi talk del PyCon, la conferenza italiana su Python
076-080:072-080 30-09-2008 16:19 Pagina 80
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
82
/Novembre 2008
JMS con Open Message Queue 4.2
L
avorando con sistemi enterprise di dimensioni
medio-grandi, spesso ci si trova di fronte a proble-
matiche riguardanti la comunicazione tra sistemi
eterogenei. Lesempio classico si ha quando necessario far
parlare una nuova applicazione con un vecchio sistema (o
sistema legacy). Lo strato software che si prende cura di que-
sti aspetti viene chiamato middleware, in quanto si
pone in mezzo alle due tecnologie per consentirne
lintero perabilit e lo scambio dati. Il ruolo principa-
le del middleware quello di fornire allo sviluppato-
re uninterfaccia di programmazione astratta. Cos
facendo, lo sviluppo applicativo diventa considere-
volmente pi semplice in quanto i dettagli di basso
livello e le problematiche di comunicazione vengo-
no mascherate dal middleware stesso.
Una risposta oramai matura ai problemi di intero-
perabilit sono i Message Oriented Middleware. Con
lacronimo MOM si soliti identificare una ben
determinata tipologia di infrastruttura software,
basata su architettura client/server, che permette la
comunicazione asincrona tra sistemi eterogenei.
Questo tipo di middleware solitamente implemen-
tato come una coda, ovvero un sistema che si occu-
pa di memorizzare i messaggi ricevuti e non ancora
consegnati e tiene traccia delle informazioni sui
messaggi gi consumati. Un messaggio pu conte-
nere sia dati che istruzioni software. Le caratteristi-
che pi importanti di un sistema MOM si possono
riassumere nei seguenti punti:
G alto livello di disaccoppiamento: il client interes-
sato a ricevere un messaggio non deve essere
necessariamente attivo durante linvio dello stes-
so. Cos come il client che spedisce non deve
aspettare alcuna risposta.
G indipendenza tra i client: i client comunicano tra
loro senza bisogno di conoscersi lun laltro.
Entrambi si connettono ad una coda e spedisco-
no/ricevono messaggi secondo un formato defi-
nito precedentemente.
G point-to-point o broadcast: possible definire,
durante la fase di configurazione, le modalit di
routing.
I primi componenti MOM rilasciati sul mercato,
nonostante garantissero l'implementazione di siste-
mi eterogenei, affidabili e flessibili, presentavano
una importante carenza: la mancanza di uno stan-
dard comune. Ognuno di questi sistemi dettava un
proprio modo di gestire i messaggi e, di conseguen-
za, una propria API. L'uso di API proprietarie crea
una dipendenza diretta tra i client ed i server che si
traduce nella modifica del codice di tutti i client nel
momento in cui si decide di sostituire MOM.
JAVA MESSAGE
SERVICE
Java Message Service lo standard, facente parte
delle specifiche di Java EE, realizzato da Sun
Microsystems, che fornisce un'astrazione indipen-
dente dal sistema per infrastrutture MOM.
Originariamente sviluppato per consentire laccesso
via Java su sistemi preesistenti, adesso largamente
adottato dai pi importanti produttori di sistemi
MOM. Cos come la tecnologia JDBC (Java Database
Connectivity) permette l'accesso ai pi diffusi data-
base attraverso una combinazione di API e linguag-
gio SQL, JMS mette a disposizione dello sviluppato-
re un'elegante interfaccia che consente la massima
portabilit verso altri sistemi basati sulla stessa spe-
cifica.
Gli oggetti con cui ci si trova a lavorare utilizzando
JMS sono:
G Connection: rappresenta la connessione attiva
tra client e server, consente il trasferimento dei
messaggi da e verso il server.
Per creare una Connection sono disponibili degli
oggetti factory raggiungibili attraverso JNDI (Java
Naming and Directory Interface).
G Session: lo stream sul quale transitano i messag-
gi. Essendo single-thread non utilizzabile in
maniera concorrente. possibile creare una
Session da una specifica Connection ed al
momento della creazione possibile specificare
alcune propriet tra le quali la transazionalit.
G Destination: rappresenta il contenitore dove si
JMS: I MESSAGGI
ASINCRONI
I SISTEMI BASATI SU CODE DI MESSAGGI OFFRONO INTERESSANTI ALTERNATIVE RISPETTO
AI TRADIZIONALI METODI DI INTEROPERABILIT IN AMBITO ENTERPRISE. A TAL PROPOSITO
SCOPRIAMO JMS CON OPEN MESSAGE QUEUE, IL PROGETTO OPEN SOURCE DI SUN
J CD J WEB
message_queue.rar
cdrom.ioprogrammo.it
Conoscenze richieste
Concetti base di
programmazione Java
Software
Java 2 Standard
Edition SDK 1.5.0 o
superiore
Impegno

Tempo di realizzazione
REQUISITI
082-086:072-080 30-09-2008 17:11 Pagina 82
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
83
G
JMS con Open Message Queue 4.2
inviano e ricevono i messaggi. caratterizzato da
un nome e una tipologia (queue o topic). Per
garantirne la portabilit anchesso raggiungibi-
le via JNDI.
G Message: lelemento che sta al centro di tutto il
sistema e rappresenta lentit che transita avan-
ti e indietro e permette la comunicazione tra il
produttore ed il consumatore del messaggio
stesso. creato dalla Session ed caratterizzato
da: header, properties e body.
G Producer: responsabile della creazione dei mes-
saggi da inviare ad una determinata Destination.
G Consumer: loggetto in ascolto su una specifica
Destination da cui ne legge i messaggi.
La specifica JMS distingue tra due diverse tipologie
di Destination: point-to-point (o queue) e publish-
subscribe (o topic). Nella prima il messaggio creato
da un Producer viene consumato da un solo
Consumer mentre nella seconda i messaggi vengono
letti da tutti i Consumer registrati.
OPEN MQ
OpenMQ limplementazione di riferimento della
specifica JMS 1.1. Il progetto, i cui dettagli sono con-
sultabili allindirizzo https://mq.dev.java.net, mem-
bro della community Glassfish. La versione utilizza-
ta in questo articolo la 4.2, ultima release stabile
rilasciata lo scorso Luglio. OpenMQ stato gi adot-
tato in molte soluzioni enterprise di larghe dimen-
sioni in quanto open-source, robusto e con una con-
sistente community alle spalle. Attualmente distri-
buito in due versioni: stand-alone o integrato allin-
terno dellapplication server Glassfish. La versione
stand-alone, che verr utilizzata nella nostra appli-
cazione dimostrativa, composta da un Messaging
Server (anche conosciuto con il nome di broker),
librerie per lintegrazione dei client (Java, C e JCA 1.5
per container JEE) ed una serie di tool per
lamministrazione. Per trasfromare i concetti finora
descritti in azioni pratiche, implementeremo una
semplice applicazione di messagistica tra utenti.
Ciascun utente potr accedere a tale servizio immet-
tendo un nome utente. Una volta connesso, avr la
possibilit di inviare messaggi di testo in modalit
privata (specificando il nome del destinatario) o
pubblica (il messaggio verr ricevuto da tutti gli
utenti connessi al sistema). Il sistema notificher
lutente alla ricezione di ogni singolo messaggio, dif-
ferenziando quelli pubblici da quelli privati. Se il
destinatario di un messaggio privato non in linea
al momento dellinvio, il sistema dovr conservare il
messaggio e recapitarlo alla successiva connessione
dellutente stesso. Lato server, configureremo il con-
tenitore di messaggi pubblici attraverso un topic e
quello per i messaggi privati con una queue.
Per quanto riguarda il client, creeremo una interfac-
cia grafica da cui inviare e ricevere i messaggi.
Nel prosieguo dellarticolo non ci soffermeremo sul
codice riguardante linterfaccia grafica poich va al
di fuori dellargomento trattato.
Prima di iniziare limplementazione necessario
avviare e configurare il server come descritto nella
sezione Configurazione del Broker.
CONNESSIONE
AL BROKER
Il primo passo quello di creare un file di configura-
zione contenente le propriet di connessione verso
il server OpenMq.
In questo modo potremmo agevolmente cambiare i
parametri di connessione senza il bisogno di modi-
ficare il codice:
mq.server.url=localhost:7676
mq.queue.name=private
mq.topic.name=public
La prima propriet indica lURL del server, mentre le
altre due contengono i nomi delle Destination dove
sono memorizzati i messaggi.
La classe MessageManager, che gestir la comunica-
zione con il server mq, avr la responsabilit di leg-
gere tali propriet durante la fase di inizializzazione.
Successivamente, queste propriet verrano utilizza-
te in fase di connessione dal metodo connect:
ConnectionFactory myFctry = new ConnectionFactory();
myFctry.setProperty(ConnectionConfiguration.imqA
COME INIZIARE
indispensabile avere installato
sul computer Java 2 Standard
Edition SDK 1.5.0 o superiore.
preferibile utilizzare l'ultima
versione disponibile sul sito
http://java.sun.com
Scaricare Open Message Queue
dal sito
https://mq.dev.java.net/downloads.
html
Attualmente, possibile
installare il prodotto su
piattaforme Linux, Solaris,
Windows e Unix. Per i primi tre
sistemi operativi sono disponibili
installer corredati di interfaccia
grafica che, attraverso un wizard
abbastanza intuitivo, guidano
l'utente step-by-step nella fase
di installazione.
Alternativamente, possibile
utilizzare il formato compresso
sotto forma di file JAR. In questo
caso sufficiente estrarre il
contenuto dell'archivio e
scaricarlo in una directory a
piacere sul file system.
Per garantire il corretto
funzionamento dei comandi di
amministrazione,
indispensabile impostare la
variabile di sistema
IMQ_JAVAHOME con il percorso
della home directory contenente
il Java Runtime Environment.
Una volta completata la fase di
installazione possibile avviare
il server attraverso lo script
imqbrokerd contenuto nella
cartella bin. Lo startup del server
pressoch immediato e riporta
a video una serie di informazioni
utili (porta di ascolto, storage
path etc.).
082-086:072-080 30-09-2008 17:11 Pagina 83
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
84
/Novembre 2008
JMS con Open Message Queue 4.2
ddressList,this.properties.getProperty("mq.server.url"));
myFctry.setProperty(ConnectionConfiguration.imqR
econnectEnabled, "true");
this.brokerConnection = myFctry.createConnection();
this.brokerConnection.start();
session = this.brokerConnection.createSession
(false, Session.AUTO_ACKNOWLEDGE);
Loggetto Connection viene creato dal
ConnectionFactory a cui viene passato lURL del
broker.
La connessione viene attivata dal metodo start() su
cui successivamente viene creata una Session non
transazionale.
INVIO E RICEZIONE
DEI MESSAGGI
Sempre all'interno del metodo connect() verrano
creati gli oggetti che gestiranno l'invio dei messaggi:
Queue privateRoom = new Queue(this.properties.
getProperty("mq.queue.name"));
this.privateSender =session.createProducer(privateRoom);
Topic publicRoom = new
Topic(this.properties.getProperty("mq.topic.name"));
this.publicSender = session.createProducer(publicRoom);
L'invio dei messaggi privati delegato al metodo
sendPrivateMessage:
AVVIO DELLA CONSOLE DI
AMMINISTRAZIONE
Avviamo la console di
amministrazione eseguendo il
comando imqadmin contenuto nella
cartella bin.
AGGIUNGIAMO UN NUOVO
BROKER
Tasto destro su Brokers _ Add Broker.
Inseriamo come label
ioProgrammoChat e settiamo le
informazioni relative alla connessione.
Prima di iniziare l'implementazione della nostra applicazione di esempio necessario configurare il server OpenMQ.
Le istruzioni sono riportate nel seguente riquadro.
CONFIGURAZIONE DEL BROKER IN SEI PASSI
NUOVE
DESTINATIONS
Tasto destro su Destination _ Add
Broker Destination. necessario
aggiungere due tipi di Destination: una
Queue (private) ed un Topic (public).
1
3 2
DEOBJECT
STORE
Tasto destro su Object Stores _ Add
Object Store. La propriet
java.naming.factory.initial
valorizzata con com.sun.jndi.fscon -
text.RefFSContextFactory.
CONNECTION
FACTORY
Tasto destro su Connection Factories
_ Add Connection Factory Object.
un passaggio fondamentale nella
costruzione del collegamento.
DESTINATION
OBJECTS
Tasto destro su Destinations _ Add
Destination Object. necessario
aggiungere un Destination Object per
ogni Destination configurata.
4 5 6
082-086:072-080 30-09-2008 17:11 Pagina 84
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
85
G
JMS con Open Message Queue 4.2
public void sendPrivateMessage(String text, String user) {
try {
TextMessage msg = session.createTextMessage();
msg.setStringProperty("recipient", user);
msg.setStringProperty("sender",this.username);
msg.setText(text);
privateSender.send(msg);
} catch (JMSException ex) {
Logger.getLogger(MessageManager.
class.getName()).log(Level.SEVERE, null, ex); }
}
Un oggetto di tipo TextMessage viene creato dalla
Session e le propriet recipient e sender vengono
valorizzate rispettivamente con i nomi utenti del
destinatario e del mittente. Analogamente il metodo
sendPublicMessage si occupa dell'invio dei messaggi
verso la Destination pubblica:
public void sendPublicMessage(String text) {
try { TextMessage msg = session.createTextMessage();
msg.setStringProperty("sender", this.username);
msg.setText(text);
publicSender.send(msg);
} catch (JMSException ex)
{
Logger.getLogger(MessageManager.
class.getName()).log(Level.SEVERE, null, ex); }
}
Una volta completato l'invio dei messaggi non ci
rimane che implementare la parte di ricezione.
Come descritto precedentemente, JMS permette lo
scambio di messaggi in modo asincrono pertanto il
nostro codice non dovr interrogare direttamente le
Destination (polling), ma verr automaticamente
notificato dal sistema della presenza di un nuovo
messaggio:
session.createConsumer(publicRoom).setMessage
Listener(this);
session.createConsumer(privateRoom, "recipient=
'"+username+"'").setMessageListener(this);
La prima istruzione crea un ascoltatore verso i mes-
saggi contenuti nel topic, mentre la seconda lo fa
sulla queue. In quest'ultima, nella parte relativa alla
creazione del Consumer, presente un parametro
aggiuntivo denominato selector, il cui compito fil-
trare i messaggi a cui siamo interessati. Il selector
definito da una stringa la cui sintassi basata su un
sottoinsieme del linguaggio SQL92. Nel nostro caso
il selector ci permette di filtrare i messaggi sulla
queue relativi all'utente attualmente connesso al
client.
Infine, la nostra classe dovr implementare
l'interfaccia javax.jms.MessageListener e definire il
metodo onMessage:
public void onMessage(Message msg)
{ ChatMessage cm = new ChatMessage();
try { cm.setPublicMsg(msg.getJMSDestination()
instanceof Topic);
cm.setUser(msg.getStringProperty("sender"));
cm.setText(((TextMessage)msg).getText()); }
catch (JMSException ex)
{
Logger.getLogger(MessageManager.
class.getName()).log(Level.SEVERE, null, ex);}
listener.messageReceived(cm);
//notifica la GUI della ricezione di un msg
}
Il Message ricevuto viene mappato all'interno di un
oggetto ChatMessage, che fa parte del model della
nostra applicazione, e viene notificato all'interfaccia
grafica che si occuper di informare l'utente dell'av-
venuta ricezione di un nuovo messaggio.
ALTRE FUNZIONALIT
Nel nostro esempio stata utilizzato il file system
per la persistenza dei messaggi. Oltre a questa
opzione, OpenMq supporta anche database relazio-
nali raggiungibili via JDBC. Inoltre, OpenMq contie-
ne una lista di funzionalit molto interessanti, che
non fanno parte della specifica JMS e che completa-
no il prodotto facilitando molte attivit. Tra queste,
una delle pi utili, sicuramente la Dead Message
Queue: una particolare coda dove vengono automa-
ticamente spostati i messaggi scaduti, ovvero non
consegnati dopo un determinato tempo (configura-
bile dalla console di amministrazione). possibile
esaminare il contenuto di questa speciale coda in
modo da monitorare ed individuare eventuali mal-
funzionamenti.
Fabrizio Fortino
NOTA
ALTRE
IMPLEMENTAZIONI
JMS OPEN SOURCE
GActiveMQ
//activemq.apache.org
G JBossMQ
//wiki.jboss.org/wiki/JBos
sMQ
G JORAM
//joram.objectweb.org
G Mantaray
//sourceforge.net/
projects/mantaray
G OpenJMS
//openjms.source
forge.net
G OSMQ
www.osmq.org
G xmlBlaster
www.xmlblaster.org
Fig. 1: La nostra applicazione di scambio messaggi in esecuzione
082-086:072-080 30-09-2008 17:11 Pagina 85
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
88
/Novembre 2008
Grafici statistici in Java grazie a JFreeChart
M
olti programmi offrono funzioni per ma-
nipolare il contenuto delle basi di dati:
dalla gestione degli ordini alle vendite,
dalla raccolta di dati scientifici alla gestione di so-
cial networks. A lungo andare, in tutte queste si-
tuazioni, si creano notevoli mole di dati e la visua-
lizzazione tramite tabelle risulta presto inadegua-
ta per chi deve avere una visione sintetica sulla
struttura. Per questo motivo la visualizzazione di da-
ti tramite grafici riassuntivi risulta essere una ca-
ratteristica fondamentale per chi deve fare analisi
e prendere decisioni. Inoltre, anche in applicazio-
ni non molto complesse, la presenza di grafici pu
apparire allutente come una funzionalit di pre-
stigio del nostro sistema.
Evidentemente, creare da zero un supporto per
rappresentare grafici, un compito che richiede
molte conoscenze di base, nonch molte risorse
in termini di sviluppo e test. Quindi, una valida al-
ternativa costituita dalluso di librerie free o com-
merciali. In questo articolo vedremo con quanta
semplicit possibile arricchire le nostra applica-
zioni con dei magnifici grafici grazie allintegra-
zione della libreria JFreeChart.
CARATTERISTICHE
DI JFREECHART
JFreeChart una libreria free realizzata interamente
in Java, che rende semplice la creazione e visua-
lizzazione di grafici allinterno di applicazioni.
David Gilbert ha rilasciato la prima versione di
JFreeChart nel 2000 e, a tuttoggi, il progetto con-
tinua a diffondersi ed evolversi con grande viva-
cit. Le principali caratteristiche di questa libreria
sono:
G Una grande variet di tipologie di grafici sup-
portati.
G Possibilit di integrazione in applicazioni client-
side e server-side.
G Integrazione in JasperReports e Struts tramite ap-
positi plug-ins.
G Possibilit di otput in vari formati tra cui swingcom-
ponents, file di immagini (PNG, JPG), file di tipo
vettoriale (PDF, EPS e SVG).
G La libreria distribuita con licenza LGPL.
Dopo esserci procurati la libreria dal sito ufficiale
vedi riquadro potremo farci unidea delle poten-
zialit di questo strumento lanciando il file jfreechart-
version-demo.jar. I grafici che possiamo rappre-
sentare sono i pi disparati: a torta, a barre, ad aree,
di tipo finanziario, di Gantt, a linee, statistici, di di-
spersione, di regressioni, serie temporali e an-
che pi divertenti come tachimetri, termometri e
orologi. Inoltre, la libreria offre grandi possibilit di
personalizzazione nei colori degli elementi e del-
lo sfondo, nei font e nel tipo di visualizzazione. Vo-
lendo potremo estendere la libreria implemen-
tando le varie interfacce, in funzione dei nostri sco-
pi.
La libreria ben strutturata, per cui, a prescinde-
re dal tipo di grafico che ci interessa realizzare,
avremo bisogno dei seguenti elementi:
G Creazione di un Dataset. Come vedremo tra bre-
ve questo elemento ci permetter di organizza-
re i dati da inserire nel grafico. La libreria forni-
sce numerose tipologie di Dataset, dovremo se-
lezionare quella adatta al nostro grafico.
G Creazione di un oggetto JFreeChart, elemento
che contiene effettivamente il grafico, tramite la
ChartFactory.
G Renderizzare il grafico e visualizzarlo o salvarlo.
A questo punto possiamo realizzare il nostro primo
grafico.
GRAFICI A TORTA
Un grafico a torta pu mostrare a colpo docchio
lincidenza dei singoli elementi sul totale, vedia-
mo come crearlo. Per prima cosa abbiamo biso-
gno di un Dataset appropriato. Tra quelli presenti
nella libreria possiamo usare il DefaultPieDataSet
UNA LIBRERIA FREE
PER CREARE GRAFICI
UN REPORT, SPESSO, DEVE ESSERE ACCOMPAGNATO DA GRAFICI, CHE MEGLIO AIUTANO A
INTERPRETARE QUELLO CHE POTREBBE SEMBRARE UN INCOMPRENSIBILE GROVIGLIO DI
NUMERI. ECCO ALLORA CHE JFREECHART PU ESSERE UNA VERA MANNA DAL CIELO
Conoscenze richieste
Java
Software
JDK, JFreeChart 1.0.9
Impegno

Tempo di realizzazione
REQUISITI
088-093:072-080 30-09-2008 16:50 Pagina 88
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
89
G
Grafici statistici in Java grazie a JFreeChart
che contiene elementi costituiti da una String uti-
le per rappresentare letichetta e un valore Dou-
ble. Vediamo come crearlo e popolarlo con alcuni
elementi:
DefaultPieDataset ds = new
DefaultPieDataset();
ds.setValue("Mele", 10.0d);
ds.setValue("Pere", 6.0d);
ds.setValue("Banane", 8.4d);
ds.setValue("Mandarini", 3.2d);
Dopo aver creato il Dataset, abbiamo inserito quat-
tro valori costituiti dal nome delletichetta e dal cor-
rispettivo valore assoluto. A questo punto creiamo il
grafico attraverso la classe statica ChartFactory. Que-
sta classe ci permette di generare tutti i grafici contenuti
nella libreria tramite metodi della forma ChartFac-
tory.createChartName. Vediamo il codice:
JFreeChart c = ChartFactory.createPieChart(
"Frutta", // titolo
ds, // dataSet
true, // legenda
true, // ToolTip
false // Locale
);
Esistono vari costruttori per ottenere uno stesso tipo
di grafico. Quello presentato uno dei pi semplici e
richiede il titolo, la sorgente di dati e una serie di flag
per impostare alcune propriet di visualizzazione.
Non ci rimane dunque che decidere come utilizzare
il nostro grafico, possiamo salvarlo come immagine
o visualizzarlo a video. Procediamo con la visualiz-
zazione a video: in questo caso JFreeChart ci viene
incontro fornendoci la classe ChartPanel che esten-
de un normale JPanel. Ecco il codice:
ChartPanel chartPanel = new ChartPanel(c,
false);
Indichiamo al costruttore il grafico che desideria-
mo visualizzare, il secondo parametro specifica se
il pannello deve essere bufferizzato. Il risultato
mostrato in figura:
Una delle caratteristiche di JFreeChart la possibilit
di visualizzare i grafici sia in modo bidimensiona-
le che tridimensionale. Infatti, sostituendo il co-
struttore della ChartFactory che abbiamo appena
usato con ChartFactory.createPieChart3De usan-
do gli stessi parametri otterremo il grafico 3D, co-
s come in Fig. 2.
Premendo il tasto destro del mouse sul pannello
che abbiamo creato, si visualizza un menua tendina
che ci permette di salvare o stampare il grafico. Ov-
viamente queste funzionalit possono essere di-
sabilitate, ma dato che non ci costano nulla, perch
non impiegarle?
Nel codice precedente abbiamo usato un Dataset
molto semplice, ma la libreria ne offre di pi evo-
luti. Unalternativa costituita da JDBCPieDataset,
in questo caso possiamo passare al costruttore del
Dataset la connessione JDBCad un database e una
stringa corrispondente allinterrogazione. La query
deve ritornare due campi, letichetta e il valore re-
lativo. Quindi, una possibile interrogazione la se-
guente:
SELECT o.cliente, SUM(o.qta) FROM ordini o GROUP
BY o.cliente
per ottenere il grafico che rappresenta la percentuale
di ordini effettuati da ogni cliente.
JFreeChart ci permette, inoltre, di aver accesso a
vari parametri di visualizzazione. Se, per esempio,
desideriamo cambiare la dimensione del font del-
le etichette e il colore di sfondo possiamo farlo at-
traverso il seguente codice:
PiePlot3D plot = (PiePlot3D) c.getPlot();
plot.setLabelFont(new Font("Sans Serif",
Font.PLAIN, 20));
plot.setLabelBackgroundPaint(Color.getHSBColor(0.6f,
0.1f, 1.0f));
plot.setBackgroundPaint(Color.CYAN);
Come si vede dal codice, recuperiamo un oggetto Fig. 1: Grafico a torta in applicazione swing
Fig. 2: Visualizzazione tridimensionale del grafico a torta
NOTA
JCOMMON
Per utilizzare JFreeChart
avremo bisogno anche della
versione adeguata di
JCommon. Solitamente la
troveremo nello stesso
pacchetto di JFreeChart.
Questa libreria contiene un
insieme di classi di utilit.
088-093:072-080 30-09-2008 16:51 Pagina 89
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
90
/Novembre 2008
Grafici statistici in Java grazie a JFreeChart
di tipo PiePlot3Ddalla nostra istanza di JFreeChart.
Dato che JFreeChart la classe che ci permette di
gestire tutti i tipi di grafico, sar necessario effettuare
un cast per ottenere il Plot adatto. A questo punto
abbiamo accesso a numerosi elementi grafici. Per
brevit nel codice ci limitiamo a modificare il back-
grounddelle label e del grafico, e impostiamo un font
pi grande per rendere pi leggibili le etichette. In
figura rappresentato il risultato:
DIAGRAMMI A BARRE
I diagrammi a barre sono impiegati in numerosi
casi. Per esempio ci permettono di visualizzare e
comparare landamento delle vendite in vari ne-
gozi in un determinato periodo. A differenza dei
diagrammi a torta, otteniamo una visualizzazio-
ne in termini assoluti, non una proporzione tra i
vari elementi. Per prima cosa creiamo il Dataset
ed inseriamo alcuni valori. Il Dataset dei diagram-
mi a barre il DefaultCategoryDataset. Questo
DataSet permette di organizzare i valori da visua-
lizzare in serie e categorie. Vediamo il codice:
DefaultCategoryDataset ds = new
DefaultCategoryDataset();
String s1 = "Mele";
String s2 = "Pere";
String s3 = "Banane";
String s4 = "Mandarini";
String c1 = "01";
String c2 = "02";
ds.addValue(10.0d, s1, c1);
ds.addValue(6.0d, s2, c1);
ds.addValue(8.4d, s3, c1);
ds.addValue(3.2d, s4, c1);
ds.addValue(9.0d, s1, c2);
ds.addValue(7.0d, s2, c2);
ds.addValue(5.3d, s3, c2);
ds.addValue(3.7d, s4, c2);
Le serie del nostro grafico composta dai nomi dei
frutti, le categorie sono i valori 01 e 02, che pos-
siamo immaginare come i mesi dellanno. Quin-
di, il grafico ci permette di vedere landamento di
quattro distinte serie in due momenti diversi. Creia-
mo ora il grafico con la ChartFactory:
JFreeChart c = ChartFactory.createBarChart3D(
"Frutti", // titolo
"Mesi", // dominio
"Qt", // range
ds, // dataSet
PlotOrientation.VERTICAL, // orientamento
true, // legenda
true, // tooltips
false // URLs
);
Come per il diagramma a torta, a questo punto
dobbiamo inserire loggetto JFreeChart nel Chart-
Panel. La figura mostra il risultato:
La correlazione esistente tra i grafici a barre e quel-
li a torta abbastanza chiara. Infatti, un grafico a
torta pu rappresentare le serie allinterno di una
determinata categoria, o una singola serie su pi ca-
tegorie. JFreeChart mette a disposizione la classe Cat-
egoryToPieDataset che ci permette di recuperare i
dati dal Dataset di un grafico a barre per ottenere
un PieDataset.
In alcuni casi la visualizzazione allinterno di unin-
terfaccia swing potrebbe non essere utile per la no-
stra applicazione. Tramite JFreeChart semplice
ottenere altri tipi di output attraverso la classe Char-
tUtilities. Ad esempio possiamo salvare unimma-
gine JPEG:
try{
ChartUtilities.saveChartAsJPEG(
new File("grafico.jpg"),
c,
700,
700);
}catch(IOException e){
Fig. 3: Accesso alle propriet di visualizzazione
Fig. 4: Diagramma a barre: serie e categorie
NOTA
DAVID GILBERT
David Gilbert, il fondatore
del progetto JFreeChart
mantiene un blog sul quale
possiamo trovare numerosi
esempi e le informazioni
relative ai nuovi rilasci:
http://www.jroller.com/dg
ilbert
088-093:072-080 30-09-2008 16:51 Pagina 90
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
91
G
Grafici statistici in Java grazie a JFreeChart
System.out.println("Errore creazione file\n"
+ e);
}
I parametri della funzione sono: il file che voglia-
mo creare, il grafico e le dimensioni dellimmagi-
ne. Un metodo equivalente saveChartAsPNG()
per salvare immagini di tipo png.
IL PIANO CARTESIANO
Sebbene non sia particolarmente complesso rea-
lizzare con java un programma in grado di rap-
presentare funzioni matematiche, potremmo vo-
ler rappresentare qualche tipo di informazione sul
piano cartesiano attraverso JFreeChart.
Per prima cosa necessario individuare il DataSet
che fa al caso nostro. Un buon punto di partenza
linterfaccia XYDataset. Tra le sue implementazio-
ni troviamo DefaultXYDataset. Si tratta di unim-
plementazione piuttosto semplice, che ci permet-
te di inserire varie serie composte da unetichetta
e da una matrice di valori. La matrice deve essere
costituita da due vettori di uguale dimensione, uno
per rappresentare il valore sulle ascisse (asse x),
laltro per il valore sulle ordinate (asse y).
Proviamo a realizzare il codice che ci permette di
rappresentare una semplice parabola di equazio-
ne y=x
2
:
DefaultXYDataset ds = newDefaultXYDataset();
int points = 101;
double[][] val = new double[2][points];
for (int i = 0; i < points; i++){
int x = i - (points/2);
val[0][i] = x;
val[1][i] = x * x;
}
ds.addSeries("Parabola", val);
Dopo aver istanziato il Dataset , decidiamo il numero
di punti che desideriamo rappresentare, e riem-
piamo la matrice con un ciclo che calcola i valori dei
vari punti da rappresentare. Ovviamente, pi fitti
saranno i punti, maggiormente definita sar la rap-
presentazione. Infine inseriamo la matrice otte-
nuta nel Dataset assegnandole unetichetta.
Non ci resta che creare il grafico:
JFreeChart c = ChartFactory.createXYLineChart(
"Piano Cartesiano", //Titolo
"X", //Label
"Y",
ds, //DataSet
PlotOrientation.VERTICAL,
//Orientamento
true, //Legenda
true, //ToolTip
false); //Url
Possiamo inserire nel grafico tutte le serie che de-
sideriamo. Ad esempio la Fig. 5 riporta un grafico
cartesiano con una parabola e una retta:
Il metodo utilizzato per rappresentare le funzioni
semplice e intuitivo, ma volendo fare un lavoro mi-
gliore sarebbe stato preferibile implementare un
XYDataset personalizzato atto a risolvere i valori
delle funzioni. Infatti, con il metodo usato, variando
il livello di zoompotremo notare che la parabola
composta da vari segmenti e che presenta un ini-
zio e una fine. Implementando il nostro Dataset
potremmo calcolare adeguatamente i punti in fun-
zione della visualizzazione.
DIAGRAMMI STATISTICI
I grafici statistici possono essere estremamente
utili per fornire una visualizzazione corretta dei
dati. Per esempio, possiamo rappresentare il valo-
re medio di una grandezza ma anche lo scarto tra
il valore minimo e massimo della grandezza al-
linterno di diverse categorie o la deviazione stan-
dard dalla media.
Proviamo a creare un diagramma a linee che rap-
presenti questi dati: come sempre, per prima cosa,
scegliamo il Dataset. Tra le classi fornite dalla li-
breria, quella che fa al caso nostro DefaultStatis-
ticalCategoryDataset. Tale classe unimplemen-
tazione di StatisticalDataset che permette, in ge-
nerale, di rappresentare molti dati statistici.
In particolare il seguente esempio crea la rappre-
sentare di una rilevazione fatta sul peso di alcuni og-
getti suddivisi in tre classi. Il Dataset ci permette
di inserire il valore medio rilevato e la deviazione
NOTA
SUL WEB
Il sito ufficiale da cui poter
scaricare la libreria e la
documentazione dellAPI :
http://www.jfree.org/jfre
echart/
NOTA
DEVIAZIONE
STANDARD
La deviazione standard o
scarto quadratico medio
un indice di dispersione,
cio una misura di
variabilit di una
popolazione intorno al
valore atteso, ha la stessa
unit di misura dei valori
osservati. Ha svariati usi,
per esempio, in ambito
finanziario, viene usata per
indicare la variabilit di
unattivit finanziaria
ovvero il suo rischio.
Fig. 5: Rappresentazione di retta e parabola sul piano
cartesiano
088-093:072-080 30-09-2008 16:51 Pagina 91
ht t p: / / www. i opr ogr ammo. i t
standard. Ecco il codice:
DefaultStatisticalCategoryDataset ds = new
DefaultStatisticalCategoryDataset();
String r1 = "Peso";
String c1 = "classe 1";
String c2 = "classe 2";
String c3 = "classe 3";
ds.add(10d, 2.2d, r1, c1);
ds.add(12d, 1.0d, r1, c2);
ds.add(9.0d, 2.0d, r1, c3);
Dopo aver istanziato il Dataset creiamo un ele-
mento di riga che rappresenta una rilevazione e
tre elementi di categoria, cio le tre classi di og-
getti su cui definita la nostra indagine. Per ogni
classe specifichiamo poi il valore della media e del-
la deviazione standard. A questo punto proviamo
a visualizzare il grafico come al solito:
JFreeChart c = ChartFactory.createLineChart(
"Visualizzazione statistica",
"Classi di oggetti",
"Peso",
ds,
PlotOrientation.VERTICAL,
true,
true,
true);
Il risultato mostrato nella Fig. 6:
Ma questo non quello che desideriamo. Infatti,
creando il grafico tramite createLineChart(), otte-
niamo un sistema che visualizza un Category-
Dataset, come quello che abbiamo usato per il dia-
gramma a barre. Noi vogliamo che il nostro grafi-
co rappresenti dei dati pi specializzati relativi al-
le statistiche. Per fare ci dobbiamo intervenire sul
sistema di visualizzazione introdotto in prece-
denza. In particolare dobbiamo recuperare lelemento
CategoryPlot e specificare un differente Renderer,
adeguato al nostro StatisticalDataset. Ecco il codi-
ce:
CategoryPlot plot = (CategoryPlot) c.getPlot();
plot.setRenderer( new
StatisticalLineAndShapeRenderer());
Lo StatisticalLineAndShapeRenderer modifica il si-
stema di visualizzazione del grafico a linee intro-
ducendo i dati statistici. Il risultato finale e corret-
to mostrato nella Fig. 7:
DIAGRAMMA DI GANTT
Il diagramma di Gantt permette di visualizzare la
strutturazione nel tempo di una determinata at-
tivit che attraversa varie fasi.
Per esempio possiamo utilizzare un diagramma
di Gantt per mostrare la richiesta di tempo per
la lavorazione di un lotto di pezzi attraverso va-
ri reparti.
Oppure possiamo utilizzarlo per descrivere la ri-
chiesta di tempo di un progetto attraverso varie
fasi: analisi, progettazione, sviluppo, testing etc.
JFreeChart ci permette di creare agevolmente
anche questa categoria di diagrammi. Per qual-
che motivo, per, i progettisti hanno pensato il
Dataset di questo tipo di grafici in un modo leg-
germente diverso. Dovremo infatti specificare i
dati direttamente nel costruttore del Dataset. Ve-
diamo un esempio:
String[] seriesNames = {"Tempo previsto"};
double[][] starts = {{1, 4, 5, 8, 13}};
double[][] ends = {{4, 5, 8, 13, 18}};
DefaultIntervalCategoryDataset ds = new
DefaultIntervalCategoryDataset(starts, ends);
ds.setSeriesKeys(seriesNames);
SISTEMA M
G
92
/Novembre 2008
Grafici statistici in Java grazie a JFreeChart
Fig. 6: Visualizzazione di dati statistici tramite LineChart
Fig. 7: Visualizzazione statistica con media e deviazione
standard
NOTA
DIAGRAMMI
DI GANTT
Il diagramma di Gantt uno
strumento di supporto alla
gestione dei progetti.
Prende il suo nome
dallingegnere statunitense
Henry Laurence Gantt che
lo ide nel 1917.
088-093:072-080 30-09-2008 16:51 Pagina 92
ht t p: / / www. i opr ogr ammo. i t
Il Dataset da utilizzare DefaultIntevalCatego-
ryDataset, che come gi il nome suggerisce ci
permette di organizzare dei dati basati su inter-
valli allinterno di categorie. Uno dei costrutto-
ri della classe prende come parametri due ma-
trici che identificano i punti di start e end delle at-
tivit nei vari reparti.
Nei vettori starts e ends abbiamo specificato una
sola serie di dati che attraversa 5 operazioni. In
particolare abbiamo la prima operazione che
inizia a 1 e finisce a 4, loperazione 2 che inizia a
4 e finisce a 5 e cos via. Si noti che il primo indice
della matrice indica la serie, mentre il secondo in-
dica lattivit.
Le matrici di inizio e fine devono avere la stessa
dimensione altrimenti si generer un Exception
a run-time.
A questo punto possiamo creare il grafico nel so-
lito modo:
JFreeChart c = ChartFactory.createGanttChart(
"Diagramma di Gantt",
"Tempo",
"Reparti",
ds,
true,
true,
false);
Il risultato mostrato in Fig. 8:
ALTRE TIPOLOGIE
DI GRAFICI
Come accennato nellintroduzione JFreeChart
permette di visualizzare anche diagrammi pi
particolari che potremmo impiegare ad esem-
pio per monitorare landamento di una variabi-
le. Un esempio di questi diagrammi costituito
dal termometro in grado di indicare la tempera-
tura. A differenza dei grafici presentati sinora,
questo diagramma viene fornito in un modo de-
cisamente differente. Non necessario creare
un Dataset e non presente nessun metodo del-
la ChartFactory in grado di creare questa tipolo-
gia di grafico.
Per creare il termometro usiamo la classe JTher-
mometer che un estensione di un JPanel, quin-
di possiamo inserire la rappresentazione diret-
tamente nellinterfaccia swing. La classe forni-
sce vari metodi per impostare il valore selezio-
nato, il range dei valori rappresentati e molto al-
tro. Ecco il codice:
JThermometer jt = new JThermometer();
jt.setBackground(Color.WHITE);
jt.setValue(22.0d);
jt.setRange(-30.0d, 50.0d);
Il risultato che si ottiene mostrato nellimmagine:
CONCLUSIONI
Inserire grafici nelle nostre applicazioni una
caratteristica che pu determinare il successo o
meno del nostro progetto. Infatti, spesso gli uten-
ti badano molto allaspetto estetico dellappli-
cazione. Inoltre, i grafici si rivelano un potente
strumento di visualizzazione in numerose si-
tuazioni.
La libreria JFreeChart si presenta ben struttura-
ta anche se alcune scelte implementative ci lasciano
perplessi, ad esempio la differente modalit di
accesso al Thermometer. Comunque eviden-
te il grande lavoro che stato svolto e le grandi po-
tenzialit di questo strumento.
Possiamo usare JFreeChart per arricchire le no-
stre applicazioni con una grande variet di gra-
fici senza bisogno di conoscenze particolari. Inol-
tre, se seguiamo il progetto scopriremo che ven-
gono rilasciati regolarmente aggiornamenti e
nuove funzionalit.
Marco Buccio
M SISTEMA
Novembre 2008/
93
G
Grafici statistici in Java grazie a JFreeChart
LAUTORE
Marco Buccio ha
conseguito la laurea
specialistica in Scienze
dellInformazione presso
lUniversit di Milano con
una tesi riguardante
sistemi di collaborazione
basati sullAugmented
Reality. Appassionato di
innovazione e tecnologia,
sviluppa applicazioni Java
e programmi per il
supporto alla didattica.
Potete contattarlo al
seguente indirizzo:
marco.buccio@gmail.com
Fig. 8: Esempio di diagramma di Gantt
Fig. 9: Il termometro visualizzabile con il pannello
JThermometer
088-093:072-080 30-09-2008 16:51 Pagina 93
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
94
/Novembre 2008
Sviluppare applicazioni per Facebook
F
acebook oramai un fenomeno di massa.
Dopo l'incredibile successo iniziale come
luogo di incontro e di ricerca di vecchie e
nuove amicizie, dal giugno 2007 l'apertura a tutti gli
utenti della piattaforma di sviluppo Facebook De-
veloper e il proliferare di piccole applicazioni, a
volte utili, altre volte dal carattere puramente ludico,
hanno fatto s che l'esperienza sul social network
diventasse varia e "magnetica".
Il fenomeno dei social network, Facebook e My-
Space fra i maggiori, interessa non a caso ai mag-
giori player del mondo dell'intrattenimento. Dal-
le ultime analisi emerso che il traffico generato da-
gli utenti dei social network supera quello genera-
to dai siti per soli adulti (fonte Click: What Millions
of People Are Doing Online and Why it Matters di
Bill Tancer)
PERCH UN'AMBIENTE
DI SVILUPPO FACEBOOK?
La possibilit di scrivere un'applicazione che vie-
ne utilizzata all'interno di Facebook, piuttosto che
realizzare un proprio sito web, offre innumerevo-
li vantaggi. Innanzitutto il login unico. L'utente
non deve compilare noiosi form di registrazione
che il pi delle volte prevedono anche una visita
alla propria e-mail, ma possono semplicemente
con un clic dare la propria adesione, utilizzare
l'applicazione e nel caso buttarla via. Questa rapi-
dit fa si' che gli utenti provino diverse applica-
zioni, dando ad ognuna la possibilit di essere co-
nosciuta e di diffondersi. L'integrazione delle ap-
plicazioni con le funzionalit del social network,
mediante strumenti che utilizzeremo in questa se-
rie di articoli, fa che l'applicazione, se ritenuta va-
lida, si diffonda in modo virale nel bacino di uten-
za Facebook che, ad oggi, conta oltre 100 milioni di
utenti nel mondo. Il tipo di diffusione possibile
viene detto "virale" perch rapportabile alla diffu-
sione dei virus come l'influenza. Una persona la
trasmette ad alcuni suoi conoscenti che a loro vol-
ta la trasmettono ad altri, ed ecco che in brevissi-
mo tempo si parla di contagio diffuso su tutto il
territorio. In questa serie di articoli vedremo qua-
li sono gli accorgimenti necessari per ridurre al
massimo la latenza della nostra applicazione.
Non mancano esempi di valide applicazioni che
si sono diffuse in modo talmente vasto da attrarre
investitori. Lo sviluppatore Craig Ulliott dopo tre set-
timane dal lancio di "Where I've been" scriveva
delle difficolt incontrate nello scalare la propria ap-
plicazione per servire i suoi 250.000 utenti regi-
strati. (fonte: http://www.insidefacebook.com/2007/06/21/i-
have-250000-users-now-what/)
Due mesi pi tardi, con una base d'utenza di 2.3
milioni di utenti, l'applicazione stata acquistata
per 3 milioni di dollari. (fonte: http://www.techcrunch.com/
2007/08/16/tripadvisor-acquires-facebook-app-where-
i%e2%80%99ve-been-for-3-million)
COME FUNZIONA
UN'APPLICAZIONE
FACEBOOK ?
Dal nostro punto di vista, un'applicazione Face-
book funziona come un normalissimo sito web.
La differenza consiste nel fatto che gli utenti non ac-
cederanno mai direttamente al nostro server, ma ci
sar sempre Facebook come intermediario tra il
browser dell'utente e la nostra applicazione.
L'utente, tramite il browser, invia una richiesta
HTTP a Facebook, il quale la processa, aggiun-
gendo dei parametri con informazioni relative al-
l'utente e la inoltra al nostro server, su una URL di
callback.
Il nostro server elabora la risposta e restituisce del-
l'HTML arricchito con dei tag speciali FBML(Face-
RUBY ON RAILS
SPOSA FACEBOOK
IN QUESTO PRIMO ARTICOLO DEDICATO ALLA PROGRAMMAZIONE DI FACEBOOK, VEDREMO
COME PREPARARE L'AMBIENTE DI SVILUPPO, CI INTERFACCEREMO CON IL SITO DI SOCIAL
NETWORKING E INIZIEREMO A INTERAGIRE CON I DATI DEGLI UTENTI
Conoscenze richieste
Ruby on Rails di base
Software
Ruby 1.8, Rails 2.1.1
Impegno

Tempo di realizzazione
REQUISITI
Fig. 1: Flusso delle richieste HTTP
094-097:072-080 30-09-2008 15:59 Pagina 94
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
95
G
Sviluppare applicazioni per Facebook
book Markup Language). Il server Facebook pro-
cessa la nostra risposta reinterpretando i tag FBML
e riportando all'utente del normalissimo HTML.
In questo processo ritorna fortemente il problema
della latenza. Dato che le richieste devono fare un
doppio passaggio rispetto a un normale sito web,
Facebook tollera dalle applicazioni un tempo di ri-
sposta di massimo 8 secondi altrimenti restituisce
un errore. In questo modo applicazioni "lente" che
darebbero un'esperienza frustrante per l'utente
vengono messe "fuori mercato" in modo naturale.
SETUP DELLAMBIENTE
DI SVILUPPO
evidente che dal punto di vista dello sviluppo
questo meccanismo ci pone dinnanzi a un pro-
blema: non possiamo sviluppare l'applicazione
Rails come siamo abituati, testando la nostra ap-
plicazione puntando il browser su http://localhost:3000.
Ma dobbiamo farlo da http://apps.facebook.com, dan-
do a Facebook la possibilit di accedere alla nostra
applicazione.
Infatti la URL di callback deve essere necessaria-
mente pubblica, cio accessibile dall'esterno.
Abbiamo a questo punto due possibilit: la prima
pi evidente utilizzare un server pubblico per lo
sviluppo. Ma una soluzione un po' scomoda, vi-
sto che ci costringerebbe a fare un deploy conti-
nuo delle modifiche che facciamo in locale. Un'al-
tra possibilit consiste nel creare un accesso su in-
ternet al nostro server locale. Anche in questo ca-
so ci sono diverse possibilit, noi utilizzeremo un
tunnel SSH.
Per fare questo necessario configurare un server
pubblico che riceve le richieste da internet e le inol-
tra nel "tunnel" che arriva fino alla nostra mac-
china. Non possedendo alcun server, possiamo
utilizzare il servizio www.tunnlr.comche stato crea-
to appositamente per facilitare lo sviluppo di ap-
plicazioni Facebook. Il servizio gratuito per 30
giorni, e successivamente richiede un pagamen-
to di 5$ al mese. La registrazione rapidissima:
aver inserito username, password ed e-mail, do-
po la consueta conferma dalla mail, ci ritroviamo
su una pagina che riporta tutte le informazioni ne-
cessarie per proseguire:
G La nostra URL pubblica (esempio http://web1.
tunnlr.com:10238 ma potrebbe essere per voi di-
versa)
G Il numero assegnato al nostro utente, lo vediamo
nell'indirizzo al quale punta il nostro browser.
G Una textarea con l'etichetta "Authorized keys"
dove dovremo inserire una chiave pubblica pre-
sente sul nostro computer.
Questo perch i dati che si spostano nel "tunnel
SSH" viaggiano su internet criptati da un algoritmo
a chiave pubblica e privata, dobbiamo quindi co-
municare al servizio la nostra chiave pubblica.
Il sito Tunnlr contiene informazioni dettagliate per
creare le chiavi e testare il tutto sia per Windows
che per Linux/Mac. Riporto qui di seguito la pro-
cedura per MAC OS X.
Da terminale, entrate nella cartella .ssh presente
nella home del vostro utente:
cd ~/.ssh
Questa cartella contiene tutte le chiavi ssh.
A questo punto dobbiamo creare una nuova chia-
ve con il comando seguente:
ssh-keygen -t dsa
il comando ci chieder il nome del file nel quale
salvare le chiavi, inseriamo tunnlr, poi ci chieder
una password, che possiamo non inserire.
A questo punto dovremmo avere due file: tunnlr
che contiene la nostra chiave privata, da non diffon-
dere mai, e tunnlr.pub che contiene la chiave pub-
blica. Quindi copiamo il contenuto di tunnlr.pubnel-
la clipboard con il comando seguente:
cat tunnlr.pub | pbcopy
ritorniamo all'interfaccia web e incolliamo nella
textarea Authorized Keys la nostra chiave pubbli-
ca. Possiamo quindi confermare con Save.
Arrivati a questo punto possiamo testare il nostro
tunnel. Baster attivarlo da console con il coman-
do seguente:
ssh -nNt -g -R :10238:0.0.0.0:3000
tunnlr001@ssh1.tunnlr.com
dove ssh1.tunnlr.come 10238 sono rispettivamen-
te host e porta della nostra URL pubblica; 0.0.0.0:3000
l'indirizzo al quale tutte le richieste devono essere
inviate e tunnlrXXX il nostro utente Tunnlr, do-
ve al posto di XXX dovete inserire il numero uten-
te.
A questo punto creiamo una nuova applicazione
Rails che chiameremo happyplanner e avviamo il
server:
rails happyplanner
cd happyplanner
script/server
Puntando il browser sulla URL pubblica dovremo
vedere il famoso "welcome aboard".
Se qualcosa andato storto, provate a ripercorre-
re il percorso relativo alle richieste che vengono
effettuate verso il vostro server locale.
NOTA
MYSPACE
MySpace stato acquistato
nel 2005 dal magnate
televisivo Rupert Murdoch
per 580 milioni di dollari, e
ad ottobre 2007 seguendo
l'esempio di Facebook ha
annunciato la propria
piattaforma di sviluppo
MySpace Developer.
094-097:072-080 30-09-2008 15:59 Pagina 95
ht t p: / / www. i opr ogr ammo. i t
SISTEMA M
G
96
/Novembre 2008
Sviluppare applicazioni per Facebook
Se siete connessi ad internet tramite un router, ad
esempio wi-fi, ricordatevi di impostare il port
forwarding per le porte che ci interessano. Nel no-
stro caso i pacchetti ricevuti sulla porta 3000 de-
vono essere sempre re-inviati (forward) sulla por-
ta 3000, all'indirizzo ip assegnato alla nostra mac-
china. Inoltre, se possedete un firewall assicurate-
vi che l'applicazione Ruby possa ricevere e inviare
sulla porta 3000.
SETUP DI UNA NUOVA
APPLICAZIONE SU
FACEBOOK DEVELOPER
Ora che la nostra macchina di sviluppo pu esse-
re raggiunta dall'esterno, dobbiamo comunicare
a Facebook la nostra intenzione di creare una nuo-
va applicazione con tutti i dettagli necessari.
Per fare questo necessario aggiungere al proprio
profilo Facebook l'applicazione "Developer".
Quindi, entrando in tale applicazione fare clic su "set-
up new application".
Come si potr osservare il form da compilare ab-
bastanza ricco di informazioni, ma per i nostri sco-
pi sufficiente compilare solo i campi fondamentali:
1. Application Name: questo il nome descrittivo
della nostra applicazione, non necessario che
sia unico
2. Callback URL: inserire qui la URL pubblica ri-
cevuta dal servizio Tunnlr. Attenzione, im-
portante digitare la barra finale nella url.
3. Canvas Page URL: questa la URL Facebook
dalla quale sar possibile accedere alla nostra
applicazione, deve essere necessariamente uni-
ca.
4. Can users add application: selezionare yes.
5. Developers: qui possibile aggiungere altri svi-
luppatori che potranno vedere l'applicazione
6. Developer Mode: spuntando questa opzione,
l'applicazione sar visibile solo agli utenti pre-
senti nell'elenco precedente.
quindi clic su Submit.
Abbiamo quindi un riepilogo dell'applicazione
creata, con due informazioni fondamentali: "API Key"
e "Secret". Queste due informazioni funzionano
come uno username e password della nostra ap-
plicazione. Mediante un algoritmo, noto come fir-
ma digitale, applicato ai parametri delle richieste
HTTP, Facebook si assicura che le richieste pro-
vengano dal nostro server e che non siano state
manomesse.
UTILIZZO DELLA
LIBRERIA FACEBOOKER
Per nostra fortuna non dobbiamo preoccuparci
dell'API di basso libello di Facebook e possiamo
concentrarci sulla nostra applicazione. Utilizzere-
mo infatti il plugin Facebooker per Rails, che fa un
ottimo lavoro nell'astrarre e rendere rails-friendly
tutte le funzionalit e i punti di integrazione con
Facebook. Aggiungiamo la libreria alla nostra ap-
plicazione:
cd happyplanner
script/plugin install
git://github.com/mmangino/facebooker.git
Questa libreria far per noi tutte le operazioni di
firma digitale necessarie per comunicare con fa-
cebook, per questo motivo, dobbiamo aggiungere
al file config/facebooker.yml API Key e Secret, oltre che
la canvas page name e la callback url:
development:
api_key: 1104966....273
secret_key: 65b60f....6fb80b
canvas_page_name: happyplanner
callback_url: http://web1.tunnlr.com:10238/
tunnel:
public_host_username:
public_host:
Fig. 2: Creazione di una nuova applicazione
Fig. 3: Riepilogo informazioni essenziali
NOTA
SOCIAL NETWORK
Il traffico generato dagli
utenti dei social network
supera quello generato dei
siti per soli adulti (fonte Clic:
What Millions of People Are
Doing Online and Why it
Matters di Bill Tancer)
NOTA
FBML
Indispensabile rifermento al
Facebook Markup
Language (FBML) il wiki
http://wiki.developers.fa
cebook.com/index.php/F
BML
094-097:072-080 30-09-2008 15:59 Pagina 96
ht t p: / / www. i opr ogr ammo. i t
M SISTEMA
Novembre 2008/
97
G
Sviluppare applicazioni per Facebook
public_port:
local_port:
in secondo luogo la libreria ci regala la possibilit
di attivare il tunneling SSH con un comodo task
rake, basta completare lo stesso file con i parame-
tri gi visti in precedenza:
tunnel:
public_host_username: tunnlr158
public_host: ssh1.tunnlr.com
public_port: 10238
local_port: 3000
quindi d'ora in poi possiamo attivare il tunnel con:
rake facebooker:tunnel:start
L'APPLICAZIONE
HAPPYPLANNER
In questa serie di articoli svilupperemo un'appli-
cazione per organizzare una serata happyhour tra
il vostro gruppo di ex compagni di scuola o tra vec-
chi colleghi di lavoro. Utilizzeremo il maggior nu-
mero di integrazioni possibili con Facebook come in-
viti, news feed e notifiche. Ogni persona invitata al-
l'happy hour potr a sua volta estendere l'invito e
ognuno potr esprimere la sua preferenza sulla da-
ta e luogo. Iniziamo quindi con il primo controller:
script/generate controller welcome
Il primo passo attivare la libreria facebooker in
tutti i controller che andremo a creare.
Per fare ci modifichiamo il file app/controlles/ap -
plication.rbaggiungendo la riga seguente:
before_filter :ensure_authenticated_to_facebook,
:set_facebook_session
Questa riga assicura che, prima di processare qual-
siasi richiesta, vengano fatti i controlli di autenti-
cazione e che venga impostato l'oggetto face-
book_sessionche contiene le informazioni relative
all'utente che accede alla nostra applicazione.
Possiamo quindi iniziare familiarizzare con l'oggetto
facebook_session, completando il file app/controllers/
welcome_controller.rbcome segue:
class WelcomeController < ApplicationController
def index
@first_name =
facebook_session.user.first_name
end
end
e creando la vista corrispondente app/views/welcome
/index.fbml.erb:
Hello <%= @first_name %>
TEST DELL'AMBIENTE
DI SVILUPPO
Possiamo quindi far partire il tunnel SSH e il no-
stro server locale rails:
rake facebooker:tunnel:start
script/server
quindi puntare il browser sulla canvas page URL
http://apps.facebook.com/happyplanner/welcome
Dovremmo quindi vedere il nostro nome, recupe-
rato dall'oggetto facebook_session. possibile re-
cuperare diverse informazioni, da tale oggetto, ma
licenza Facebook vieta di memorizzare su un no-
stro database tali informazioni. Molte applicazio-
ni usano queste informazioni, mostrando contenuti
specifici a seconda delle preferenze dell'utente.
Da questo momento in poi, possiamo iniziare a
sperimentare a volont. Ad esempio aggiungendo
al nostro controller le righe seguenti possiamo leg-
gere le preferenze cinematografiche dell'utente:
facebook_session.user.populate(:movies)
@movies = facebook_session.user.movies
Possiamo anche iniziare a sperimentare qualche
tag FBML, ad esempio potremmo dare a Facebook
il compito di recuperare e renderizzare l'immagine
dell'utente nella pagina di benvenuto.
Nel controller recuperiamo un'informazione fon-
damentale, il facebook_id dell'utente:
@user_facebook_id =
facebook_session.user.facebook_id
E nella vista aggiungiamo il seguente tag speciale
FBML:
<fb:profile-pic uid="<%= @user_facebook_id %>" />
Notate che in questo caso, noi non conosciamo
l'immagine dell'utente, Facebook che sostitui-
sce a questo tag speciale l'HTML necessario a vi-
sualizzare l'immagine utente con tanto di colle-
gamento al profilo. Nel prossimo articolo rende-
remo funzionate l'applicazione happyplanner
creando le pagine FBML per invitare nuovi amici e
consentirgli di indicare la loro preferenza su data
e luogo.
Giancarlo Valente
NOTA
RECUPERARE
GLI USER DATA
Alcuni esempi di user data
che possiamo leggere
tramite l'oggetto
facebook_session.user
sono: books, interests,
music, religion, political.
La lista completa
recuperabile nella
documentazione di
Facebooker
http://facebooker.rubyfor
ge.org/
094-097:072-080 30-09-2008 15:59 Pagina 97
ht t p: / / www. i opr ogr ammo. i t
DATABASE M
G
98
/Novembre 2008
Da tabelle, righe e colonne al Domain Model
L'
approccio tradizionale al design e allo
sviluppo della applicazioni sempre
stato quello di effettuare l'analisi del
problema per poi procedere con la progettazione
del database, spesso il vero cuore dell'applicazio-
ne. Tuttavia la progettazione e lo sviluppo del
codice, ormai da diversi anni a questa parte,
sono attuati con linguaggi di programmazione
fortemente basati sulla programmazione ad
oggetti. Tale approccio consente di trattare in
modo pi naturale il problema. Ad esempio, la
tipica fattura non sar pi gestita come righe
sparse su un database relazionale pi o meno
relazionate tra loro, con salti mortali per aggan-
ciare le varie parti del singolo dato per effetto
delle doverose normalizzazioni del database. Un
formato gerarchico come XML o un modello
concettuale basato sulla classe Fattura che espo-
ne un elenco di classi RigaFattura e che ha come
cliente una propriet Cliente di tipo Anagrafica
ben pi naturale perch pi si avvicina alla
maniera di concettualizzare la realt per oggetti
indipendenti, autoconsistenti e dotati di com-
portamento specifico che operata dal cervello
umano.
IL PROBLEMA DELLA
IMPEDANCE MISMATCH
Tale modalit di progettazione basata su classi di
entit davvero comoda, ma si rivela fragile nel
momento in cui le classi, che rappresentano il
cosidetto Domain Model, devono essere persisti-
te o rilette da un database relazionale, e cio
quando dal modello concettuale (le entit) biso-
gna passare al modello fisico (il database). Esiste
una teoria che da anni studia la difficolt (che
talvolta diventa l'impossibilit) di passare da un
modello all'altro che va sotto il nome di
Impedance Mismatch; esiste una documentazio-
ne e una bibliografia molto vasta sull'argomento.
Gli argomenti cardine di questa teoria sono:
G l'incapsulamento tipico dell'approccio OOP
che garantisce una consistenza dei dati molto
inferiore all'integrit referenziale tipica dei
database relazionali;
G i database relazionali sono privi di concetti
quali classi di oggetti (ad esempio, tipi diversi
di fatture con un numero differente di campi e
di relazioni a seconda del tipo), ereditariet e
polimorfismo;
G differenze nei tipi di dati e cio la necessit di
mappare i tipi di dati dei database relazioni su
quelli del linguaggio di programmazione e del
type system dell'ambiente di esecuzione;
G differenze strutturali, a partire dal differente
approccio tra il modello gerarchico OOP con
livelli di nidificazione dei dati molto pronun-
ciati e il modello relazionale che , invece,
tipicamente orizzontale;
G il modello di interrogazione dei dati che, nei
relazionali semplice e potente (SQL), ma che
nel modello OOP spesso del tutto assente o
comunque ben pi limitato, tuttavia negli
ultimi anni si sono affacciate soluzioni per
ovviare al problema e Linq forse la pi nota;
G il concetto di transazionalit nativo nei
database relazionali e il suo uso talmente
naturale che non farne uso forse pi difficile
che il suo contrario; nel mondo OOP della
programmazione per domain model questo
supporto del tutto assente se non costruen-
do soluzioni ad hoc, spesso posticce e poco
efficaci.
GLI ORM
E I MULINI A VENTO
Date le premesse, sembrerebbe quasi che non ci
sia modo di lavorare fattivamente con un
domain model e che si debba, ahinoi, restare
sotto il giogo di tabelle, righe, colonne, relazioni
ipernormalizzate e quant'altro per realizzare le
nostre brave applicazioni gestionali. Tuttavia gli
INTRODUZIONE
A NHIBERNATE
GLI OBJECT-RELATIONAL MAPPING, SONO SISTEMI SOFTWARE IN GRADO DI EFFETTUARE
AUTOMATICAMENTE LA MAPPATURA TRA UN DB E LE STRUTTURE TIPICHE DI UN
LINGUAGGIO DI PROGRAMMAZIONE. UNO DI QUESTI, PER .NET, NHIBERNATE
J CD J WEB
nhibernate.zip
cdrom.ioprogrammo.it
Conoscenze richieste
.NET livello intermedio
Software
Microsoft Visual Studio
Express 2008
Impegno
Tempo di realizzazione
REQUISITI
098-103:101-105 30-09-2008 16:54 Pagina 98
ht t p: / / www. i opr ogr ammo. i t
M DATABASE
Novembre 2008/
99
G
Da tabelle, righe e colonne al Domain Model
informatici sono tipi tenaci e portati alle sfide
impossibili: ed ecco la trovata degli ORM, gli
Object-Relational Mapping, cio sistemi software
in grado di effettuare automaticamente la map-
patura tra i due mondi risolvendo, cos, la mag-
gior parte dei problemi enunciati nel triste elen-
co precedente. Il loro principio di funzionamen-
to molto semplice: si scrive il proprio sistema di
class entit (il domain model), si disegna il data-
base relazionale che ne gestir la persistenza e si
istruisce l'ORM su come mappare i due mondi.
Tale operazione tipicamente avviene definendo
la mappatura, cio una sorta di metadefinizione,
spesso in formato XML, di come passare dal
mondo fisico al mondo concettuale. In taluni
casi opzionalmente possibile saltare la defini-
zione del modello fisico perch, definendo il
sistema di classi e la mappatura, alcuni ORM
sono in grado di generare lo schema fisico in
modo automatico e di aggiornarlo a seguito dei
mutamenti del modello di mappature.
NHIBERNATE:
IL PRINCIPE DEGLI ORM
NEL MONDO .NET
NHibernate forse il pi sofisticato ORM per
.NET attualmente disponibile: derivato dai
concetti e dagli algoritmi del suo celebre padre
Hibernate per il mondo Java, ma soprattutto con
la versione 1.2 e l'imminente 2.0, ha subto una
riprogettazione per sfruttare pienamente le
caratteristiche native e talvolta uniche del
mondo .NET. Dunque non si tratta di uno dei
tanti porting di codice da Java in circolazione ma
di un prodotto vero per .NET. Microsoft sta da
anni lavorando ai suoi tanti ORM: Linq for SQL,
ADO Entity Framework, ecc.., ma forse il pi
significativo che rapprsenter forse la prima vera
alternativa ad NHibernate sar proprio
l'imminente ADO Entity Framework.
Ma concentriamoci su NHibernate partendo dal
suo link: http://www.hibernate.org/343.html, dove
possibile scaricarne l'ultima versione stabile (al
momento in cui scriviamo), la 1.2.1.G.A. del
26/11/2007. La sua installazione richiede la pre-
senza del Microsoft .NET Framework 1.1 o suc-
cessivi, ma consigliabile la presenza almeno
della versione 2.0 per sfruttarne i potenti generi-
ci. Non viene installato nulla nella GAC e per uti-
lizzarlo nelle nostre applicazioni sufficiente
riferire una manciata di assembly.
Tutta questa potenza di fuoco gratuita perch il
progetto rilasciato, anche in sorgente, con
licenza LGPL. Il mantainer del progetto Jboss,
la societ dell'omonimo application server J2EE,
di recente passata ad un nuovo proprietario: Red
Hat, l'azienda della famosa distribuzione Linux.
UN ESEMPIO REALE
Dopo tante chiacchiere, passiamo a studiare un
esempio reale osservando il case study per eccel-
lenza fornito dai contributor di Nhibernate:
l'applicazione Order System. Useremo tutti pro-
dotti completamente gratuiti per lo sviluppo del-
l'esempio:
G Microsoft Visual C# 2008 Express Edition
(download da http://www.microsoft.com/Express/);
G Microsoft SQL Server 2005 Express Edition
(stesso link);
G NHibernate 1.2.1.G.A.
L'applicazione fornisce un semplice gestionale
fatto di anagrafiche fornitori, prodotti e ordini e
si bassa su cinque entit: Supplier, Important -
Supplier, Manufacturer, Product e Sale.
Osserviamo la definizione di tali classi, a comin-
ciare dalla classe fornitore (Supplier) e della sua
specializzazione ImportantSupplier:
//entit fornitore
public class Supplier
{
private int supplierId;
//Id del fornitore
public virtual int SupplierId
{
get { return this.supplierId; }
set { this.supplierId = value; }
}
private System.Collections.IList productList;
//elenco dei prodotti offerti dal fornitore
public virtual System.Collections.IList ProductList
{
get { return this.productList; }
}
private string name;
//nome del fornitore
public virtual string Name
{
get { return this.name; }
set { this.name = value; }
}
public Supplier()
{
this.supplierId = 0;
098-103:101-105 30-09-2008 16:54 Pagina 99
ht t p: / / www. i opr ogr ammo. i t
this.productList = new
System.Collections.ArrayList();
this.name = string.Empty;
}
}
//entit derivata dei fornitori, con dati addizionali
public class ImportantSupplier : Supplier
{
private string address;
public virtual string Address
{
get { return this.address; }
set { this.address = value; }
}
}
Possiamo notare che si tratta di normali classi C#
senza particolari fronzoli. Da rilevare la forte
gerarchizzazione della struttura: Supplier, ad
esempio, espone un ArrayList di Product.
Possiamo scrivere questa propriet anche usan-
do un generico e cio come:
private IList<Product> productList;
public virtual IList<Product> ProductList
{
get { return this.productList; }
}
In tal caso, per, instanzieremo una relativa List
generica:
this.productList = new List<Product>();
Si noti che ImportantSupplier una specializza-
zione di Supplier a cui aggiunge alcuni campi.
Dal punto di vista del database relazionale que-
sto il tipico caso da risolvere con due tabelle,
una principale (SUPPLIER) ed una con i campi
aggiuntivi della specializzazione (IMPOR-
TANTSUPPLIER) in relazione uno a uno tra loro.
Ed proprio quello che faremo con il nostro
database SQL Server di esempio, come mostrato
in Fig. 1.
IL MAPPING
DI NHIBERNATE
Non ci resta che osservare come mappare queste
due entit con i file di mapping di NHibernate.
Tanto per cominciare ogni entit ha il suo file di
mappatura corrispondente che per convenzione
si chiama come l'entit stessa seguito dall'esten-
sione .hbm.xml (ad esempio Supplier.hbm.xml):
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-
mapping-2.2" auto-import="true" default-
access="nosetter.camelcase">
<class name="OrderSystem.Objects.Supplier,
OrderSystem.Objects" table="SUPPLIER" lazy="true"
mutable="true">
<id name="SupplierId" unsaved-value="0"
column="ID_SUPPLIER" type="System.Int32">
<generator class="hilo"/>
</id>
<property name="Name" column="NAME" not-
null="true" type="System.String" />
<bag name="ProductList" table="PRODUCT"
cascade="all" fetch="join" inverse="true">
<key column="FK_SUPPLIER" />
<one-to-many class="OrderSystem.
Objects.Product, OrderSystem.Objects" />
</bag>
</class>
</hibernate-mapping>
Lo scopo dell'articolo non descrivere approfon-
ditamente ogni singolo aspetto e attributo di
NHibernate e delle sue mappature, dato che esi-
ste una documentazione molto ricca che assolve
a questo compito.
Ci concentreremo sugli aspetti pi importanti.
Per cominciare possiamo notare il nodo class che
riporta alcuni interessanti attributi:
G name, che rappresenta il full qualified name
dell'entit e l'assembly in cui definita, sepa-
rate da virgola;
DATABASE M
G
100
/Novembre 2008
Da tabelle, righe e colonne al Domain Model
Fig. 1: Il diagramma del database Order System
098-103:101-105 30-09-2008 16:54 Pagina 100
ht t p: / / www. i opr ogr ammo. i t
G table, il nome della tabella fisica sul database
relazione usata per persistere i dati di questa
entit;
G lazy, un attributo booleano che, se attivo, con-
sente il caricamento on demand dei dati dalla
tabella, una sorta di cursore lato server, in
modo da migliorare nettamente le prestazioni
perch si evita il precaricamento di tutti i dati
all'inizio della richiesta.
Sebbene siano consentite chiavi composite,
fortemente consigliato, se non quasi obbligatorio
in NHibernate (ma anche come buona regola di
normalizzazione dei database), che ciascuna
entit abbia come chiave primaria un campo
numerico. Tale campo definito attraverso il tag
id. Nell'esempio viene individuato con l'at -
tributo name il campo SupplierId dell'entit
come chiave e l'equivante campo ID_SUPPLIER
della tabella SUPPLIER attraverso l'attributo col-
umn.
L'attributo type, infine, descrive il tipo del campo
usando il type system di .NET.
Si noti che presente il sottonodo generator a
indicare l'algoritmo di generazione. In tal caso
viene usato hilo, un sistema basato su una
tabella contatore (HIBERNATE_UNIQUE_KEY)
che eroga id univoci per ciascuna entit. Tuttavia
si pu sempre optare per la pi classica identity
di SQL Server, in tal caso il generator diventa:
<generator class="native" />
Tutte le propriet scalari dell'entit, che quindi
coincidono con i campi fisici della tabella, sono
invece identificati nella mappatura attraverso il
nodo property, in cui ritroviamo in modo abba-
stanza intuitivo gli attributi name, column, not-
null e type, rispettivamente ad indicare il nome
della propriet della classe, il nome della colon-
na nella tabella, la possibilit che il valore sia null
e il suo tipo.
Il nodo bag introduce la prima relazione tra
entit e la sua mappatura sul modello fisico.
In particolare si tratta di una relazione uno a
molti. Nello specifico la propriet ProductList,
introdotta in precedenza. Un Supplier fornisce
un elenco di Product e pertanto nella tabella
PRODUCT (attributo table) vi una foreign key
verso la tabella SUPPLIER con il campo
FK_SUPPLIER (attributo column del sottonodo
key). La morfologia dell'entit Product non
chiaramente indicata, se non il full qualified
name (tag one-to-many) perch la sua definizio-
ne presente nel file Product.hbm.xml.
Con la mappature ImportantSupplier.hbm.xml si
definisce l'entit ImportantSupplier, derivata da
Supplier i cui campi aggiuntivi sono presenti
nella tabella IMPORTANT_SUPPLIER.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-
mapping-2.2" auto-import="true" default-
access="nosetter.camelcase">
<joined-subclass
name="OrderSystem.Objects.ImportantSupplier,
OrderSystem.Objects"
extends="OrderSystem.Objects.Supplier,
OrderSystem.Objects" table="IMPORTANT_SUPPLIER"
lazy="true">
<key column="ID_IMPORTANT_SUPPLIER" />
<property name="Address" column="ADDRESS"
not-null="true" type="System.String" />
</joined-subclass>
</hibernate-mapping>
Questa relazione, che dal punto di vista del
domain model di ereditariet/specializzazione
e dal punto di vista del modello fisico un rela-
zione uno a uno tra due tabelle, rappresentata
con il tag joined-subclass che definisce gli attri-
buti name (la classe e l'assembly dell'entit deri-
vata) ed extends (la classe e l'assembly dell'entit
padre).
Per il resto la mappatura ricorda quella vista in
precedenza, con i suoi tag key property, bag, etc...
Per completezza riportiamo l'entit Product e
ometteremo Manufacturer e Sale che per pos-
sono essere consultate direttamente dai sorgenti
allegati all'articolo.
//entit Prodotto
public class Product
{
private int productId;
public virtual int ProductId
{
get { return this.productId; }
set { this.productId = value; }
}
private Iesi.Collections.ISet manufacturerSet;
//elenco dei produttori che producono questo
M DATABASE
Novembre 2008/
101
G
Da tabelle, righe e colonne al Domain Model
Fig. 2: Un esempio di mappatura visto da Visual C# 2008 Express Edition
098-103:101-105 30-09-2008 16:54 Pagina 101
ht t p: / / www. i opr ogr ammo. i t
prodotto
public virtual Iesi.Collections.ISet ManufacturerSet
{
get {
return this.manufacturerSet;
}
}
private string name;
public virtual string Name
{
get {
return this.name;
}
set {
this.name = value;
}
}
private Supplier supplier;
//fornitore del prodotto
public virtual Supplier Supplier
{
get {
return this.supplier;
}
set {
this.supplier = value;
}
}
}
Si osservino la propriet Supplier, di tipo
Supplier, che nel modello fisico rappresentato
con una relazione molti a uno tra SUPPLIER e
PRODUCT attraverso il campo FK_SUPPLIER
presente nella seconda tabella e che di fatto
una foreign key verso SUPPLIER.
Tali informazioni sono definite nella mappatura
attraverso il tag many-to-one.
Ecco di seguito il mapping di Product:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-
mapping-2.2" auto-import="true" default-
access="nosetter.camelcase">
<class name="OrderSystem.Objects.Product,
OrderSystem.Objects" table="PRODUCT" lazy="true"
mutable="true">
<id name="ProductId" unsaved-value="0"
column="ID_PRODUCT" type="System.Int32">
<generator class="hilo"/>
</id>
<set name="ManufacturerSet"
table="PRODUCT_X_MANUFACTURER"
cascade="save-update" lazy="true" inverse="true">
<key column="FK_PRODUCT" />
<many-to-many
class="OrderSystem.Objects.Manufacturer,
OrderSystem.Objects" column=
"FK_MANUFACTURER" />
</set>
<property name="Name" column="NAME" not-
null="true" type="System.String" />
<many-to-one name="Supplier"
column="FK_SUPPLIER" not-null="true"
fetch="join" cascade="save-update" />
</class>
</hibernate-mapping>
Un ultima relazione da osservare forse anche la
pi sorprendente che riesca a gestire
NHibernate, la many-to-many, cio la molti a
molti che nel modello fisico si rappresenta con
una tabella di relazione.
Nel caso specifico le tabelle interessate sono
PRODUCT e MANUFACTURER e la tabella di
relazione PRODUCT_X_MANUFACTURER.
Tra le varie relazioni del modello relazionale que-
sta probabilmente la meno naturale, la pi
distante rispetto a quanto si faccia con il domain
model. Infatti, se osserviamo le nostre entit,
non esiste alcuna entit ProductManufacturer,
ma l'entit Product espone semplicemente una
propriet Manufacturer Set che altro non che
una collezione di Manufacturer, implicitando
cos la tabella di relazione.
Tutto questo possibile a costo zero grazie ad
NHibernate.
CASCADE E FETCH
I vari tag di relazione espongono tutti quanti una
coppia di tag fetch e cascade. Essi sono molto
interessanti.
Il primo pu assumere i valori select e join.
Nel primo caso viene eseguita una select specifi-
ca per recuperare l'entit in relazione, nel secon-
do caso viene effettua semplicemente un'unica
select tra la tabella dell'entit principale e quella
dell'entit in relazione ritornando tutti i campi
dell'una e dell'altra e consentendo, cos, di popo-
lare le due entit con un solo accesso fisico al
database.
Il secondo tag cascade, indica invece le tipologie
di operazioni che possono essere effettuate sulla
tabella in relazione a partire dall'entit padre.
Il concetto ricorda da vicino quello dell'omoni-
ma funzionalit presente nei database relaziona-
li. E quindi la possibilit di riportare in cascata
update, delete, rimozione di righe orfane e altro
ancora.
DATABASE M
G
102
/Novembre 2008
Da tabelle, righe e colonne al Domain Model
098-103:101-105 30-09-2008 16:54 Pagina 102
ht t p: / / www. i opr ogr ammo. i t
Si raccomanda la consultazione della documen-
tazione specifica per un approfondimento.
CODICE DI ESEMPIO
Il seguente codice mostra come configurare ogni
aspetto di NHibernate per l'uso nella nostra
applicazione. I commenti aiutano la compren-
sione del codice:
private static NHibernate.Cfg.Configuration cfg = null;
private static NHibernate.ISessionFactory
sessionFactory = null;
static NHibernateSessionFactory()
{
//oggetto di configurazione di NHibernate
cfg = new NHibernate.Cfg.Configuration();
// assegnazione dei provider di accesso ai dati
cfg.Properties.Add(NHibernate.Cfg.Environment.
ConnectionProvider,
"NHibernate.Connection.DriverConnectionProvider");
cfg.Properties.Add(NHibernate.Cfg.Environment.
ConnectionDriver,
"Nhibernate.Driver.SqlClientDriver");
//versione di SQL Server da utilizzare
cfg.Properties.Add(NHibernate.Cfg.Environment.
Dialect,
"Nhibernate.Dialect.MsSql2000Dialect");
// consente un illimitato numero di outer join
cfg.Properties.Add(NHibernate.Cfg.Environment.
MaxFetchDepth, "-1");
// connection string
cfg.Properties.Add(NHibernate.Cfg.Environment.
ConnectionString,
"Server=vex;initial catalog=OrderSystem;User
Id=sa;Password=atreides");
// assembly delle entit
cfg.AddAssembly(Assembly.LoadFrom("OrderSystem.
Objects.dll"));
sessionFactory = cfg.BuildSessionFactory();
}
public static NHibernate.ISession OpenSession()
{
// apertura della sessione
return sessionFactory.OpenSession();
}
Ma veniamo finalmente agli aspetti pi interes-
santi della soluzione e cio osserviamo quanto
sia semplice e naturale creare e modificare entit
demandando completamente all'ORM ogni ac -
ces so fisico al database per la loro persistenza:
public int SaveObjectAndReturnSupplier(bool
makeImportant, int prodCount)
{
//creazione della sessione di NHibernate
ISession session =
NHibernateSessionFactory.OpenSession();
ITransaction transaction =
session.BeginTransaction();
string timeStamp =
DateTime.Now.ToString("yyyyMMdd-HHmmss");
// crea un ImportantSupplier
Supplier s = makeImportant ? new
ImportantSupplier("supplier " + timeStamp, "n/a") :
new Supplier("supplier " + timeStamp);
// crea un Manufacturer
Manufacturer m = new
Manufacturer("manufacturer " + timeStamp);
// crea i prodotti da legare al produttore
for (int i = 0; i < prodCount; i++)
{
Product p = new Product();
p.Name = "product " + timeStamp;
p.Supplier = s; s.ProductList.Add(p);
// sync both ways
m.ProductSet.Add(p);
p.ManufacturerSet.Add(m);
}
try
{
Console.WriteLine("Salvataggio modifiche...");
// tutti i cambiamenti vengono persistiti perch
si impostato un Cascade di tipo all
session.Save(s);
transaction.Commit();
Console.WriteLine("Save succeded");
}
catch {
transaction.Rollback();
Console.WriteLine("Save failed"); throw;
}
finally {
session.Close();
}
return s.SupplierId;
}
Ed ora, invece, vediamo come rileggere le entit
dal database:
public void ListSupplierProductsAndManufacturers(int
supplierId)
{
ISession session =
NhibernateSessionFactory.OpenSession();
//recupero di un Supplier con un Id specifico dal
database
Supplier s =
(Supplier)session.Get(typeof(Supplier), supplierId);
//NHibernate si preoccupa di verificare che si tratti
M DATABASE
Novembre 2008/
103
G
Da tabelle, righe e colonne al Domain Model
098-103:101-105 30-09-2008 16:54 Pagina 103
ht t p: / / www. i opr ogr ammo. i t
di un Supplier semplice o di un
//ImportantSupplier verificando semplicemente
l'esistenza di una riga relazionata nella tabella
//IMPORTANT_SUPPLIER, in tal caso viene
istanziata l'entit derivata e la select eseguita sul
database
//recupera anche tali informazioni e li inserisce
nelle propriet specifiche dell'entit derivata
Console.Write(s.Name + " is ");
Console.WriteLine(s is ImportantSupplier ?
"important" : "normal");
Console.WriteLine("Products...");
foreach(Product p in s.ProductList)
{
Console.WriteLine("\t" + p.Name);
foreach (Manufacturer m in p.ManufacturerSet)
Console.WriteLine("\t\t" + m.Name);
}
session.Close();
}
Ed ecco come modificare entit esistenti relazio-
nandole ad altre nuove o pre-esistenti:
public void AddSaleForEachProduct(int supplierId)
{
ISession session =
NhibernateSessionFactory.OpenSession();
//caricamento del Supplier dall'id specificato
Supplier s =
(Supplier)session.Load(typeof(Supplier), supplierId);
Console.WriteLine("Adding sales...");
//per ogni prodotto fornito dal Supplier e quindi
presente nella collezione ProductList
//si procede alla creazione di un'entit di vendita
(Sale) ad esso relazionata
foreach (Product p in s.ProductList)
{
Sale sale = new Sale(new Random().Next(), p,
1, DateTime.Now);
//l'entit viene salvata nella transazione
corrente
session.Save(sale);
}
Console.WriteLine("Saving sales");
session.Flush();
}
QUERY COMPLESSE CON
MODELLO A OGGETTI
DEI CRITERIA
I Criteria sono un importante funzionalit di
NHibernate che consente di effettuare interroga-
zioni complesse sul domain model.
importante comprendere che l'interrogazione
viene effettuata dal programmatore sul modello
concettuale di entit e che poi l'ORM a preoc-
cuparsi di trasformare tali richieste in tradiziona-
li query SQL atte al popolamento delle entit
richieste dall'utente.
L'esempio commentato di seguito offre un inte-
ressante caso di studio:
public void SimpleExamples()
{
ISession session =
NHibernateSessionFactory.OpenSession();
ICriteria criteria; IList result;
//creazione del Criteria sull'entit Sale
criteria = session.CreateCriteria(typeof(Sale));
// aggiunta delle condizioni sull'interrogazione che
sono espresse direttamente sulle propriet dell'entit
// e delle sotto entit del grafo principale. Tali
relazioni sui sottografi devono essere espressamente
// indicate creando dei sub Criteria
ICriteria subCriteria = criteria
.CreateCriteria("Product")
.CreateCriteria("Supplier")
.Add(Expression.Like("Name","supplier%"));
// ordinamento del risultato
criteria.AddOrder(NHibernate.Expression.
Order.Asc("SaleDate"));
//paginazione
int currentPage = 0, pageSize = 5;
criteria
.SetFirstResult(currentPage * pageSize)
.SetMaxResults(pageSize);
// eventuale modalit di lock in caso di
aggiornamento
criteria.SetLockMode(LockMode.Upgrade);
// esecuzione dell'interrogazione
result = criteria.List();
// NHibernate provveder a generare ed eseguire
per noi una query SQL piuttosto complessa in grado
di soddisfare l'interrogazione
/*
* SELECT top 5 this.ID_SALE as ID_SALE2_,
this.FK_PRODUCT as FK_PRODUCT2_, this.PRICE as
PRICE2_, this.QUANTITY as QUANTITY2_,
this.SALE_DATE as SALE_DATE2_, x0_.ID_PRODUCT
as ID_PRODUCT0_, x0_.FK_SUPPLIER as
FK_SUPPL3_0_, x0_.NAME as NAME0_,
x1_.ID_SUPPLIER as ID_SUPPL1_1_, case when
[x1__1_].ID_IMPORTANT_SUPPLIER is not null then 1
when x1_.ID_SUPPLIER is not null then 0 end as
clazz_1_, x1_.NAME as NAME4_1_,
[x1__1_].ADDRESS as ADDRESS5_1_
* FROM SALE this
* inner join PRODUCT x0_ on
this.FK_PRODUCT=x0_.ID_PRODUCT
DATABASE M
G
104
/Novembre 2008
Da tabelle, righe e colonne al Domain Model
098-103:101-105 30-09-2008 16:54 Pagina 104
ht t p: / / www. i opr ogr ammo. i t
* inner join SUPPLIER x1_ on
x0_.FK_SUPPLIER=x1_.ID_SUPPLIER
* left outer join IMPORTANT_SUPPLIER
[x1__1_] on x1_.ID_SUPPLIER=[x1__1_].
ID_IMPORTANT_SUPPLIER
* WHERE x1_.NAME like @p0
* ORDER BY this.SALE_DATE asc
*/
}
HQL: HIBERNATE
QUERY LANGUAGE
L'ORM offre una modalit pi comoda e natura-
le per effettuare le interrogazioni, un vero e pro-
prio linguggio simil SQL, ma ad oggetti perch
orientato a descrivere interrogazioni sul domain
model.
Tutto ci che si pu fare scrivendo codice di
Criteria possibile anche farlo scrivendo una
semplice query HQL.
concettualmente simile al nuovo linguaggio
Linq di Microsoft. Eccone una serie di esempi
convincenti tratti dall'applicazione didattica
Order System:
public void SimpleExamples()
{
ISession session =
NHibernateSessionFactory.OpenSession();
IQuery query;
IList result = null;
// semplice interrogazione su Sale per ricavare il
campo Product.Name con una condizione di like
sullo stesso campo
query = session.CreateQuery("select s.
Product.Name from Sale s where s.Product.Name
like:filterName");
query.SetString("filterName", "%");
query.SetFirstResult(5).SetMaxResults(10);
result = query.List();
Program.ListResult("IQuerySamples.
SimpleExamples1", result);
// esempio di join tra entit
query = session.CreateQuery("select p.Name from
Supplier s inner join s.ProductList p");
result = query.List();
Program.ListResult("IQuerySamples.
SimpleExamples2", result);
// query con HAVING COUNT
query = session.CreateQuery("from Sale s where
size(s.Product.ManufacturerSet) = 1");
result = query.List();
Program.ListResult("IQuerySamples.
SimpleExamples4", result);
// esempio complesso: recuperare tutti i
manufacturer i cui prodotti sono stati venduti
query = session.CreateQuery(
@"select distinct m.Name from Sale s inner
join s.Product.ManufacturerSet m
where s.SaleDate in (:allowedDates) order by
m.Name");
query.SetParameterList("allowedDates",
new DateTime[] { new DateTime(2000, 01,
01), DateTime.Now.Date });
result = query.List();
Program.ListResult("IQuerySamples.
SimpleExamples5", result);
// uso di funzioni statistiche nelle query HQL
query = session.CreateQuery("select max
( s.SaleDate ) from Sale s");
object uniqueResult = query.UniqueResult();
}
CONCLUSIONI
Molti concetti sono stati tralasciati e tante nuove
scoperte e funzionalit saranno magicamente a
vostra disposizione semplicemente studiando
un po' la documentazione ufficiale del prodotto
e, soprattutto, cominciando a usarlo per costrui-
re esempi via via pi complessi.
Gli ORM sono certamente la nuova frontiera
delle applicazioni gestionali e colmano final-
mente le lacune e le differenze, spesso concet-
tuali, tra il mondo fisico dei database relazionali
e il comodo mondo del disegno di applicazioni
per entit logiche.
Certo, la curva di apprendimento del prodotto
allinizio particolarmente ripida e manca la
tipica sensazione di sentirsi sempre a casa che si
prova quando si usano i ben integrati prodotti
Microsoft, ma i benefici che avrete dal suo studio
e dalla sua adozione vi ripagheranno in poco
tempo dello sforzo iniziale.
Vito Vessia
M DATABASE
Novembre 2008/
105
G
Da tabelle, righe e colonne al Domain Model
Fig. 3: Liste di entit popolate con interrogazioni all'ORM
098-103:101-105 30-09-2008 16:54 Pagina 105
ht t p: / / www. i opr ogr ammo. i t
SOFTWARE SUL CD M
G
106
/Novembre 2008
Librerie e Tool di sviluppo
OXYGEN CODE
GENERATOR 1.7
GENERATORE DI CODICE
PER .NET
Il modo migliore per sottrarsi allobbligo
di scrivere codice di routine. Particolar-
mente indicato per generare il codice ne-
cessario ad accedere e gestire database. Tra
le funzioni di output sono incluse stored
procedure e una classe data provider
che semplifica il collegamento a databa-
se. Versione trial.
Directory: OxyGen-
Code_TrialVersion_1_7_7.zip
CSHARP CODE
LIBRARY 1.9.0.146
UNA LIBRERIA IMPERDIBILE E
RICCHISSIMA DI CODICE PER C#
Una libreria di codice che comprende ol-
tre 50.000 righe, facilmente consultabile
grazie al motore di ricerca integrato. Par-
ticolarmente curata la sezione dedicata
alla stampa.
Directory: csharp_code_library.zip
VB 6 ANTIDECOM-
PILER 2.3.1
UNA UTILITY PER PROTEGGERE
I TUOI PROGRAMMI
Il programma ideale per gli sviluppatori
VB che hanno il timore che qualcuno co-
pi il codice da loro creato con tanto fa-
tica. VB 6 AntiDecompiler, infatti, di-
sabilita la decompilazione nativa di Vi-
sual Basic, che sar possibile effettua-
re in seguito soltanto con i tool messi a
disposizione dalla software house pro-
duttrice del programma. Una volta che
SOFTWARE
SUL CD
Tji Java IDE 1.6
F
esteggia dieci anni di attivit questo IDE per
programmare in Java che fa della semplicit
d'utilizzo uno dei suoi punti di forza. svilup-
pato direttamente in Java, per utilizzarlo c' bi-
sogno di avere installato almeno Java 1.6. Molto
personalizzabile, permette di salvare, compila-
re ed eseguire i programmi con pochi clic del
mouse. Integra uno swing GUI designer per fa-
cilitare la creazione di interfacce grafiche e un
utilissimo correttore della sinstassi in realtime,
che eviter errori proprio mentre si sviluppa.
L'autocompletamento permette di accelerare il la-
voro di stesura del codice, cos come
l'autoindentazione mantiene ordinate e ben leg-
gibili le migliaia di righe di codice sviluppate. Il
layout del programma molto personalizzabile,
per mettere quanto pi posssibile a proprio agio
l'utilizzatore.
106-108:106-108-software 30-09-2008 16:02 Pagina 106
ht t p: / / www. i opr ogr ammo. i t
M SOFTWARE SUL CD
Novembre 2008/
107
G
Librerie e Tool di sviluppo
VB Antidecompiler intervenuto su
un file eseguibile, l'output dell'even-
tuale decompilazione produrr sol-
tanto del confuso assembler, o addi-
rittura non produrr nessun output.
Directory: setup_vb6ad
VIRTUAL BOX 2.0.2
TESTARE I NOSTRI PROGRAMMI
SU TUTTI I SISTEMI OPERATIVI
Chi sviluppa per pi di un sistema operativo
ha spesso lesigenza di testare le proprie
applicazioni, ma pu non avere la possi-
bilit di installare pi di un SO, sulla pro-
pria macchina. Virtual Box permette di
utilizzare molti sistemi operativi sulla
stessa macchina, senza avere il bisogno
di partizionare lhard disk e configurare il
sistema in maniera particolare. Buona la
gestione delle porte USB della macchina
host, cos come anche quella della rete
virtuale, che pu essere bridged o anche
NAT.
Directory: VirtualBox-1.6.2-Win_x86.zip
ENTERPRISE
ARCHITECT 7.1
BUILD 832
PER PROGETTARE IN UML
LA TUA APPLICAZIONE
Questo programma sar certamente gra-
dito a chi utilizza UML durante lo svi-
lupppo delle sue applicazione e progetti.
Enterprise Architect permette, infatti, gra-
zie ad un'interfaccia molto semplice da
utilizzare, di modellare, disegnare e co-
struire un software o un progetto busi-
ness. Il programma riesce a generare il
codice in molti linguaggi, tra i quali Ac-
tionscript, Java, C#, C++ e molti altri, com-
presi Python e DDL). Grazie a questo
software pi semplice passare agevol-
mente dalla fase di progettazione a quel-
la di sviluppo, accelerando notevolmen-
te i tempi di lavorazione del progetto.
Directory: easetup.exe
SHOW ME THE
TEMPLATE
REALIZZARE INTERFACCE
FACILMENTE CON WPF
Un tool che permette di esplorare i tem-
plate e tutti i panel che possibile gesti-
re tramite WPF. Una ottima occasione per
studiare il funzionamento di WPF e non
solo: i template pre-impostati, consen-
tono di realizzare linterfaccia delle no-
stre applicazioni pi semplicemente e
senza dover partire da zero.
Directory: ShowMeTheTempla-
te.zip.zip
XF ULTRASCALE
2008 1
FORMATTAZIONE XML
ULTRAVELOCE
Un processore di XML capace di format-
tare migliaia di pagine al minuto, grazie al-
la particolare cura posta nellottimizza-
zione delle eleaborazioni. Tra i principa-
li vantaggi, oltra alla menzionata velo-
cit, si segnala la piccola impronta in me-
moria (generalmente sotto i 100 MB) e la
capacit di lavorare perfettamente in con-
testi ad elevato parallelismo.
Directory: XFUltrascale-Win32.1.0.0.zip
TIERDEVELOPER
.NET 6
SCRIVI LE TUE APPLICAZIONI
IN MET TEMPO
Un efficace generatore di codice capa-
ce di produrre sia applicazioni ASP.NET
sia Windows Forms. Copre un ampio
spettro di utilizzo e pu ridurre il tem-
po di sviluppo dal 50 al 70%.
Directory: Tierdevnetent.exe
EXCELSIOR JET 6.4
DA JAVA AD ESEGUIBILI
WINDOWS E LINUX
Una soluzione completa per accelerare
e proteggere il tuo codice Java. Permette
di generare codice applicazioni esegui-
bili per piattaforme Windows e Linux.
La virtual machine integrata fa s che sul
PC non debba essere preinstalla la JVM. Ver-
sione di prova a trenta giorni.
Directory: Jet-latest-eval-win32.exe
HEXASSISTANT 2.6
EDITOR ESADECIMALE
FULL OPTIONAL
Un editor esadecimale che permette
di effettuare il dump ed esportarne il
risultato nel codice sorgente C o in Ja-
va. Sono disponibili due visualizzato-
ri (Data Quick e Structure) che per-
mettono di visualizzare e interpretare
correttamente i dati ricavati.
Directory: HexAssistant_Setup26
NOTEPAD++ 4.7.5
PICCOLO E VELOCISSIMO
Un editor completamente gratuito (con
licenza GPL) che supporta numerosi
linguaggi di programmazione. C, C++,
Java, C#, XML, HTML, PHP, Javascript,
ASP, VB/VBS, SQL, ASP, VB/VBS, SQL e
altri ancora. Tra le caratteristiche pi
interessanti, un ottimo Syntax Highli-
ghting, la possibilit di effettuare ri-
cerche con le regular expression e la
capacit di stampare il codice con gli
stessi colori presentati a video.
Directory: npp.4.7.5.Installer.exe
JDK 6 UPDATE 7
LINDISPENSABILE AMBIENTE
DI RUN TIME JAVA
106-108:106-108-software 30-09-2008 16:02 Pagina 107
ht t p: / / www. i opr ogr ammo. i t
SOFTWARE SUL CD M
G
108
/Novembre 2008
Librerie e Tool di sviluppo
Il Java SE Development Kit (JDK) non
pu mancare nel PC del bravo svilup-
patore. Una volta installato, si disporr
del completo ambiente per eseguire le
applicazioni Java (il Java Runtime En-
vironment) oltre al solito, potentissi-
mo tool da linea di comando utile an-
che per testare le applet sviluppate per
i propri siti Web.
Directory: jdk-6u7-windows-i586-p.exe
A4DESK PRO
UN SITO FLASH PROFESSIONALE
IN POCHI CLIC
Potente e semplice da usare, A4DeskPro
un tool per la progettazione di siti web
in Flash, ricchi di animazioni, artico-
lati menu a scorrimento e tutti i tipici ele-
menti Flash che sono la gioia di molti
utenti (e il terrore di molti altri: atten-
ti al consumo di banda!). La program-
mazione avviene tutta per via visuale
e non richiede conoscenze specifiche.
Versione di prova.
Directory: a4deskpro_setup.exe
VOIP SDK
APPLICAZIONI CHE PARLANO
Una soluzione per includere nelle no-
stre applicazioni capacit di Voice Over
IP. LSDK include un client ad alta effi-
cienza che garantisce una ottima qua-
lit audio anche in caso di scarsit di
banda. Compatibile con il protocollo
H323, permette di realizzare applica-
zioni per chiacchierare sia in locale che
su scala mondiale, via internet.
Directory: H323_20.zip
PPL - POCKET
PROGRAMMING
LANGUAGE
PROGRAMMAZIONE MOBILE
PPL un linguaggio di programmazio-
ne orientate agli oggetti, molto sem-
plice da imparare. PPL gira su Smartpho-
ne, PocketPC e PC, cos i programmi
scritti in PPL sono compatibili al 100%
con tutte le piattaforme supportate.
PPL dotato di un completo ambiente
di sviluppo e fornisce anche degli stru-
menti verticali per la realizzazione di
applicazioni specifiche come i giochi. Tra
i grandi vantaggi di PPL c lestrema
leggerezza dellambiente di sviluppo:
anche un portatile non particolarmen-
te performante si dimostra pi che suf-
ficiente.
Directory: PPL152.exe
IRON SPEED
DESIGNER 5.2
UN MAGO PER GENERARE AP-
PLICAZIONI GESTIONALI IN .NET
Un generatore di applicazioni che per-
mette di creare in pochissimo tempo
gestionali a partire da database gi esi-
stenti: sufficiente indicare la base di da-
ti per avere in pochi istanti una appli-
cazione Web 2.0 perfettamente funzio-
nante. Lapproccio wizard-driven di
estrema semplicit e permette di en-
trare in confidenza con lapplicazione
in poco tempo.
Il codice .NET generato aperto al cen-
to per cento e pu dunque essere ulte-
riormente manipolato per ottenere ri-
sultati pi raffinati.
Directory: Iron_Speed_Designer_Setup.exe
ALTOVA DATABA-
SESPY 2008 REL. 2
PROGGETTARE E INTERROGARE DB
Dai creatori di XMLSpy, un comodissi-
mo tool per la progettazione e
linterrogazione di database. Si inter-
faccia con tutti i principali DB e sem-
plifica enormemente la scrittura di query
SQL. Versione di prova a trenta giorni.
Directory: databasespy2008.exe
ANDROID SDK
LA PIATTAFORMA DI SVILUPPO
PI ATTESA
Qualsiasi mossa di Google di fonda-
mentale importanza per chiunque ope-
ri nel campo dellinformatica, e per gli
sviluppatori in particolare. Android, la
piattaforma di Google per gli smartpho-
ne della prossima generazione, pro-
mette di rivoluzionare lintero settore.
Non facciamoci cogliere impreparati:
installiamo il kit di sviluppo, emulia-
mo la piattaforma e assimiliamo i con-
cetti fondamentali della programma-
zione.
Directory: android-sdk-windows-0.9_beta
106-108:106-108-software 30-09-2008 16:02 Pagina 108
ht t p: / / www. i opr ogr ammo. i t
SOLUZIONI M
G
110
/Novembre 2008
Algoritmi geometrici per la programmazione grafica raster
C
ontinuiamo lesplorazione dei metodi
alla base della grafica computazione.
Dopo aver trattato la rasterizzazione, ora
ci occuperemo di una tecnica altrettanto rilevan-
te: il clipping, ovvero il pre-trattamento applica-
to alle primitive grafiche, come ad esempio la
rappresentazione di un segmento, per
lindividuazione della parte che effettivamente
bisogna mostrare. In considerazione del fatto
che, la fase di modellazione potrebbe generare
primitive che risulterebbero totalmente o in
parte fuori dallarea di rappresentazione; come
per un segmento che solo parzialmente ricade
nella finestra grafica attiva. Come detto si tratta
di un procedimento che viene implementato a
monte della fase di rasterizzazione (scan conver-
sion) che cos risulta snellita. Un altro approccio,
che si occupa dello stesso problema, conosciuto
come scissoring, prevede lindividuazione delle
parti da rappresentare, punto per punto assieme
alla fase di scan conversion. stato mostrato che
conveniente, in termini computazionali, svi-
luppare prima il clipping, nellambito delle ela-
borazioni geometriche e successivamente la
scan conversion. Esamineremo in questo artico-
lo i principali metodi di clipping applicati a pri-
mitive elementari: punto, segmento e poligono.
PIPELINE GRAFICA
la scomposizione in fasi che realizza lintero
processo grafico dalla modellazione alla rappre-
sentazione. Essa consiste nellindividuazione
degli oggetti espressi da insiemi di primitive gra-
fiche che si vogliono rappresentare. In particola-
re verranno specificati insiemi di vertici che
descrivono gli oggetti. Tali oggetti vengono trat-
tati geometricamente per risolvere una serie di
problemi che usualmente si incontrano, data la
complessit del compito, tale fase stata scom-
posta in sotto fasi: clipping, trasformazioni
proiezioni, eliminazione superfici nascoste e
ombre. Il clipping, oggetto delle presenti analisi,
consiste nella rimozione di parti di primitive che
non appartengono alla finestra di visualizzazio-
ne. Una seconda sotto fase la trasformazione da
spazio 3D dove spesso un oggetto descritto in
spazio 2D dove deve essere rappresentato
(appunto perch disponiamo di schermi bidi-
mensionali!); fanno parte di questo stadio altre
trasformazioni come le proiezioni. La terza e la
quarta sotto fasi consistono nella rimozione
delle aree non visibili e la visualizzazione delle
ombre. A tale proposito, utile ricordare che un
oggetto nello spazio viene visto differentemente
e comunque parzialmente a seconda della pro-
spettiva dellosservatore. Anche la visione delle
ombre, ovviamente, sono differenti a seconda
della prospettiva. Le altre fasi della pipeline gra-
fica sono la rasterizzazione o scan conversion
che abbiamo esaminato negli scorsi numeri e
infine la rappresentazione vera e propria.
CLIPPING, I PRIMI PASSI
Abbiamo detto che le tecniche di clipping consi-
stono nella rimozione di primitive grafiche o
parti di esse, che non rientrano nellarea di rap-
presentazione conosciuta come finestra di clip-
ping. Le primitive sono semplici figure geometri-
che (le complesse si costruiscono come insieme
di primitive). Larea di clipping consiste in una
figura geometrica come un rettangolo, un esae-
dro o in generale un poligono convesso; nella
pratica si usa generalmente un rettangolo, come
uno schermo o una finestra canvas.
Nella nostra trattazione ci riferiremo sempre ad
un rettangolo. In Fig. 1 si comprende meglio cosa
intendiamo per clipping. Il rettangolo indica la
finestra di rappresentazione.
Supponiamo che il rettangolo di clipping sia
descritto da quattro valori, due delimitazioni per
le x (xmax e xmin) e due delimitazioni per le y
(ymax e ymin). Il caso pi semplice di clipping
IL CLIPPING
DI UNA IMMAGINE
SI TRATTA DI UNA FASE FONDAMENTALE NEL TRATTAMENTO GEOMETRICO DI UNIMMAGINE.
CONSENTE DI INDIVIDUARE LE PARTI DI OGGETTI CHE EFFETTIVAMENTE DEVONO ESSERE
TRACCIATE NELLA FINESTRA DI SCHERMO DISPONIBILE. ESAMINIAMONE IL FUNZIONAMENTO
Conoscenze richieste
Nozioni di base su:
linguaggi di
programmazione,
geometria e analisi
matematica.
Software
-
Impegno

Tempo di realizzazione
REQUISITI
110-114:110-113-Soluzioni mcd 30-09-2008 16:43 Pagina 110
ht t p: / / www. i opr ogr ammo. i t
M SOLUZIONI
Novembre 2008/
111
G
Algoritmi geometrici per la programmazione grafica raster
riguarda un singolo punto o pixel. Esso verr rap-
presentato se si trova allinterno del rettangolo.
Verificarlo banale. Devono essere soddisfatte
disequazioni elementari:
Il passo successivo consiste nel clipping di un
segmento. I casi che possono presentarsi sono
tre, a seconda se il segmento interno in tutte le
sue parti allarea, se completamente esterno o
se solo parzialmente presente nella finestra di
visualizzazione. Una classificazione pi interes-
sante fatta in funzione degli estremi del seg-
mento:
a Entrambi gli estremi sono interni allarea,
allora il segmento interno;
b Un estremo interno e laltro esterno, allo-
ra il segmento interseca un lato del rettangolo;
c Entrambi gli estremi sono esterni. In tal caso
possono aversi due situazioni: la prima (c.1)
che il segmento completamente esterno, la
seconda (c.2) che il segmento per una parte
interno. Vedremo che i vari algoritmi possono
distinguere ancora altri casi sulla base del
modello geometrico adottato per la descrizione
del problema. Le quattro differenti situazioni
sono rappresentate in Fig. 2: A) segmento con
entrambi i vertici interni; B) segmento con un
estremo interno e laltro esterno al rettangolo di
clipping; C.1) entrambi gli estremi sono esterni e
il segmento esterno; C.2) entrambi sono ester-
ni, ma il segmento, in parte, ricade nellarea di
visualizzazione
Alla base dei vari metodi che esamineremo per la
realizzazione del clipping vi la descrizione del
rettangolo di clipping come lintersezione di
quattro rette a coppie di due parallele allasse
delle ascisse e delle ordinate. Le quattro rette
citate hanno le seguenti equazioni:
y=ymax
y=ymin
x=xmax
x=xmin
In Fig. 3 possiamo vedere la rappresentazione
delle quattro rette.
Un primo metodo per individuare se il segmento
o parte di esso interno allarea di clipping, il
pi ovvio, ma come vedremo poco efficiente. Si
tratta di una soluzione analitica che prevede il
sistema tra lequazione della retta associata al
segmento e le quattro rette che delimitano larea
di clipping. Sicuramente si riscontreranno delle
intersezioni; bisogner per verificare se effetti-
vamente intersecano il rettangolo di clipping. Si
procede in questo modo. Si descrive la retta asso-
ciata ad un segmento A, B (ossia individuato da
due punti A e B) con le equazioni parametriche:
Con q appartenente allintervallo chiuso [0, 1].
Si tratta di sostituire le quattro coordinate cor-
rispondenti ai due punti A e B. Si ottengono
due equazioni: una per la x e una per la y; le
NOTA
POLIGONO
CONVESSO
Un poligono convesso se.
presi due qualsiasi punti, il
segmento che li unisce
ricade interamente nella
figura (poligono). Si tratta
del tipo di figura geometrica
pi facile da trattare
nellambito del clipping.
Fig. 1: A) oggetti da rappresentare prima del clipping;
B) oggetti rappresentati con il clipping
Fig. 2: Quattro differenti tipi di segmenti
Fig. 3: Descrizione dellarea di clipping come interse-
zione di quattro rette
110-114:110-113-Soluzioni mcd 30-09-2008 16:43 Pagina 111
ht t p: / / www. i opr ogr ammo. i t
SOLUZIONI M
G
112
/Gennaio 2006
Fisica
ht t p: / / www. i opr ogr ammo. i t
G
112
/Novembre 2008
Algoritmi geometrici per la programmazione grafica raster
indicheremo con equazioni 1. Andando a
sostituire ad x e y la prima volta x
max
e y
max
e
la seconda volta x
min
e y
min
si ottengono
valori di q che sostituiti nelle equazioni 1 indi-
viduano i punti di intersezione. Leventuale
parallelismo che non produce intersezione va
verificato prima che si imposti il sistema.
Bisogna poi verificare se i punti di intersezione
appartengono al rettangolo di clipping. Tale
metodo costoso in termini computazionali,
poich richiede delle divisioni in virgola mobi-
le. Per cui si preferiranno metodi, come quello
proposto di seguito, per i quali lintersezione
viene effettuata quando effettivamente indi-
spensabile.
METODO
COHEN SUTHERLAND
Lidea alla base del metodo omonimo ridurre al
minimo il calcolo delle intersezioni. Per farlo si
numerano le aree risultanti dal partizionamento
con una sequenza di bit, come possiamo vedere
in figura; essendo nove sono necessari 4 bit. La
numerazione avviene comparando la posizione
di un qualsiasi punto con le quattro rette che
delimitano il rettangolo. I valori sono descritti in
sintesi nella seguente tabella.
Ognuna delle nove aree sar quindi descritta da
una sequenza di bit cosi come si pu desumere
da quanto riportato in Fig. 4.
La sequenza prende il nome di outcode.
Si pu notare che il rettangolo di clipping carat-
terizzato da outcode di quattro zeri. Ad esempio:
tutte le tre aree sopra la retta di delimitazione
superiore, sono caratterizzate dal valore 1 del
primo bit. Si tratta di esaminare gli estremi del
segmenti. Con operazioni logiche booleane pos-
siamo stabilire per alcuni casi se il segmento
interno o esterno allarea di clipping. I restanti
casi di indeterminatezza possono essere trattati
analiticamente con il calcolo delle eventuali
intersezioni che consentiranno di accorciare il
segmento.
I passi dellalgoritmo sono riportati di seguito.
1. Calcolo degli outcode per gli estremi del seg-
mento;
2. Verifica se il segmento interno allarea di
clipping, si ha se lor logico dei due estremi
restituisce la sequenza nulla;
3. Verifica se il segmento esterno allarea di
clipping, si ha se land logico dei due estremi
diverso da zero, almeno in una componente;
4. Per i restanti casi, per i quali land logico ugua-
le a zero, necessario calcolare le eventuali
intersezioni e accorciare il segmento;
I vari casi che si possono verificare sono riporta-
ti sempre in Fig. 5, ad ogni categoria di segmenti
associato un colore. Il segmenti al punto 2,
ossia interni sono blu. I segmenti al punto 3,
ossia esterni sono rossi. Infine, i segmenti al
punto 4 che generano indeterminatezza, sono
riportati in arancio. Tutti i segmenti che hanno
un estremo allinterno dellarea di clipping e
laltro allesterno, ovviamente ricadono nel quar-
to caso, land sicuramente una sequenza di
zeri, essendo larea citata tutta nulla. Per il quar-
to caso bisogna parlare di eventuali intersezio-
ni proprio perch ci troviamo in una situazione
di indeterminatezza e quindi non siamo certi che
vi siano; infatti, tra i segmenti arancio ve ne sono
due che sono esterni. Merita approfondimento
limplementazione del punto quattro.
Esaminando i bit discordanti dalloperazione di
and logico si possono calcolare pi rapidamente
le intersezioni. Indichiamo i due punti con A e B,
e supponiamo di disporre di una funzione che
restituisce liesimo bit delloutcode per un gene-
rico punto P, del tipo outcode(i,P). Le rette sono
numerate. Con tali presupposti si procede con lo
sviluppo dellalgoritmo:
1. i <-- 1
2. Ripeti
3. Se outcode(i,A) <> outcode(i,B) calcola inter-
sezione con retta(i)
4. Accorcia il segmento con lintersezione otte-
nuta al punto precedente, ottieni il punto C;
BIT - VALORE BIT 1 0
b0 y>ymax y<=ymax
b1 y<ymin y>=ymin
b2 x>xmax x<=xmax
b3 x<xmin x>=xmin
Fig. 4: Suddivisione in nove aree e outcode relativi
110-114:110-113-Soluzioni mcd 30-09-2008 16:43 Pagina 112
ht t p: / / www. i opr ogr ammo. i t
5. Se outcode(j,C) OR outcode(j,B) = zero per j
che va da 1 a 4 allora esci altrimenti vai al
punto 6;
6. i <-- i+1
7. finch i>4
Alcune precisazioni. Il punto C ottenuto al passo
4, che ci consente di accorciare il segmento, va a
sostituire un estremo del segmento che potr
essere il punto A o il punto B a seconda della
intersezione ottenuta. Ad esempio, lintersezione
con la retta superiore y=ymax sostituisce A, men-
tre lintersezione con y=ymin sostituisce B.
Per semplicit nellalgoritmo, al punto successi-
vo era riportato al posto di A. Analogo ragiona-
mento per le rette verticali. Inoltre si suppone
sempre che i punti A e B vengano assegnati
seguendo alcune regole come ya>yb. Lalgoritmo
funziona anche se il segmento non ha intersezio-
ni con larea di clipping, in tal caso viene accor-
ciato a tal punto da essere annullato.
In definitiva, stabilito che lalgoritmo di Cohen
Sutherland sicuramente pi efficiente del
metodo puramente analitico, riporta buone
performance nei casi in cui molti segmenti siano
esterni.
METODO LIANG BARSKY
Questo secondo metodo affronta il problema
con una logica differente, che stato dimostrato
essere pi efficiente. Il primo passo esprimere il
segmento descritto dai due punti A e B, con una
retta nella forma parametrica che abbiamo
espresso per il metodo analitico.
Valori di q tra 0 e 1 ci fanno spostare nel segmen-
to da A a B. I punti del segmento interni al rettan-
golo di clipping soddisfano la relazione:
che in funzione della prima espressione ed espri-
mendo le differenze tra le x e y come incrementi
delta, possono essere riscritte:
Un modo sintetico per riscrivere queste quattro
disequazioni :
Definiamo le due serie di quattro valori u e v, che
verranno numerati con parametro k da 1 a 4.
Associate ai quattro vincoli, le quattro rette:
(1) (sinistra) x=x
min
(2) (destra) x=x
max
(3) (basso) y=y
min
(4) (alto) y=y
max
Le linee parallele sono individuate da alcuni
valori di u
k
pari a 0. Per capire se in questo caso
una linea interna o esterna baster osservare i
corrispondenti (rispetto a k) valori di v, se sono
minori di 0 il segmento completamente ester-
no, altrimenti interno. Esaminiamo il caso pi
comune di non parallelismo. I parametri u con-
tengono una preziosa informazione, se sono
negativi ci indicano che nel muoverci sul seg-
mento da A a B andiamo da dentro a fuori, men-
tre se sono positivi nello stesso movimento vuol
dire che si va da fuori a dentro.
A questo punto, per individuare leventuale seg-
mento di clipping, bisogna trovare due valori di
q, uno di entrata, laltro di uscita. Il primo, quello
di entrata, il valore massimo tra lo 0 i, qk cor-
rispondenti ad entrate (con valori di uk<0).
Lo 0 va considerato poich il valore minimo che
pu assumere q. Analogamente, il parametro q,
che indica la fine del segmento nella finestra di
visualizzazione, il minimo tra 1 e i valori di q
uscenti (corrispondenti a uk>0). Il valore massi-
mo che pu assumere q 1.
qe = max{0, tj con j corrispondente a uj<0}
qu = min{1, tj con j corrispondente a uj>0}
Siamo quasi al termine. Il segmento andr rap-
presentato solo se lentrata prima delluscita,
ovvero se qe<qu, in caso contrario il segmento
esterno e quindi non va visualizzato. In Fig. 5
mostrato il caso di segmento parzialmente inter-
no. Inoltre, sono riportati in grigio dei segmenti
che indicano i meccanismi di variazione del
parametro u.
Infine, solo se necessario, verranno calcolate le
intersezioni che si ottengono facilmente dal rap-
porto vk/uk. Tale metodo sembra complesso, ma
in realt non cos. Tale impressione potrebbe
M SOLUZIONI
Novembre 2008/
113
G
Algoritmi geometrici per la programmazione grafica raster
110-114:110-113-Soluzioni mcd 30-09-2008 16:43 Pagina 113
ht t p: / / www. i opr ogr ammo. i t
essere dovuta alla maggiore quantit di dati pro-
dotti, che in definitiva per semplificano il pro-
cedimento e soprattutto ne decretano rilevanti
prestazioni, tanto da preferirlo a quello di Cohen
Sutherland. Ad ogni modo una comprensione
maggiore si ottiene se si sviluppa, anche con
carta e penna un esempio completo, riportando
da prima i valori che delimitano la finestra di
clipping (xmin, xmax, ymin e ymax), poi indivi-
duando i due punti A e B e poi applicando il
metodo descritto.
SUTHERLAND
HODGAMAN
Il clipping di un poligono unoperazione pi
complessa, proprio perch ci troviamo di fronte
a una figura geometrica pi articolata. Inoltre, i
poligoni concavi possono produrre aree separate
di visualizzazione.
I due casi sono rappresentati in Fig. 6.
Con figure convesse, per risolvere il problema,
basterebbe applicare uno dei due metodi di clip-
ping lineari associati ai segmenti che delimitano il
poligono e riempire opportunamente le parti inter-
ne ad esso. Il problema si pone per le figure non
convesse. Per risolverlo Sutherland e Hodgaman
hanno messo a punto un algoritmo che propone
un nuovo approccio alla problematica. Si basa sulla
pi volte usata strategia di divide et impera. Si trat-
ta di risolvere diversi e circoscritti problemi che
associati ci forniscono la soluzione finale. Tale pro-
cedimento riportato in Fig. 7.
Il problema basilare il clipping rispetto ad un solo
lato descritto da una retta infinita. Si ripete cos per
i quattro lati una sorta di eliminazione delle parti
superflue. Ad ogni iterazione, per tale problema
basilare si individuano due semipiani, uno interno,
laltro esterno. Si confrontano in ordine gli n vertici
del poligono. Si costruisce una lista di output costi-
tuita da punti. Il confronto si effettua per coppie di
vertici che potranno trovarsi rispetto ai due piani
definiti:
- entrambi nel semipiano interno, allora il secondo
viene aggiunto nella lista di output;
- il primo interno e il secondo esterno, allora
lintersezione viene aggiunta alla lista di output;
- entrambi esterni, allora non si fa nulla;
- il primo esterno ed il secondo interno, allora ven-
gono aggiunti sia lintersezione sia il punto nella
lista di output.
Si costruisce cos un nuovo poligono per ogni lato
che viene esaminato. come se, ad ogni iterazione,
andassimo con una gomma a cancellare tutto ci
che nel lato esterno rispetto a quello che stiamo
esaminando. In alcuni casi il clipping d luogo a
pi componenti, generando falsi spigoli. Sar una
elaborazione a posteriori a risolvere questa situa-
zione. Anche per questo metodo consigliabile un
test anche solo con carta e penna, ci aiuter a capi-
re in profondit i meccanismi di funzionamento.
CONCLUSIONI
Unaltra importante tessera, che ci porter a una
completa e approfondita conoscenza dei metodi
geometrici usati per la computer grafica basilare,
stata mostrata.
Fabio Grimaldi
SOLUZIONI M
G
114
/Novembre 2008
Algoritmi geometrici per la programmazione grafica raster
Fig. 5: Rappresentazione dellalgoritmo di Liang
Barsky
Fig. 6: Clipping di poligoni: A) poligono convesso;
B) poligono non convesso, pu generare pi aree
Fig. 7: Clipping con metodologia divide et impera
110-114:110-113-Soluzioni mcd 30-09-2008 16:43 Pagina 114

You might also like