Share |

sabato 27 marzo 2010

Java: Sintassi dei JavaDocs

In questo articolo saranno spiegate le regole della sintassi JavaDoc e la corretta modalità di approccio alla creazione di documentazione HTML tramite l'utilizzo del parser JavaDoc Tool.

Aggiungere commenti nel codice sorgente di ogni classe Java, ma anche di routines COBOL o di sorgenti C ecc., è sempre stata un'attività molto importante per ogni sviluppatore.

Aver documentato bene il codice sorgente dei propri programmi, per esempio, risulta essere stata un mossa fondamentale ed efficace nel momento in cui torneremo a modificare, o implementare, quel progetto; diviene, invece, necessario ricorrere ad un tipo diverso di documentazione, cioè al di fuori dei sorgenti, quando si stanno realizzando delle API che successivamente saranno utilizzate da altri sviluppatori in altri progetti, a meno che non si voglia rendere pubblico il codice sorgente delle proprie API.

E' in questo secondo ambito che il JavaDoc Tool esprime le sue potenzialità perchè parsando l'intera struttura di packages di un progetto Java è in grado di estrapolare i commenti aggiunti al codice sorgente e, creando la documentazione HTML, di organizzarli in una fitta rete di link HTML.

Nota

Questo documento, esclusi i riferimenti storici, è il riassunto in Italiano dell'articolo How to Write Doc Comments for the Javadoc Tool.

Terminologia

Per facilitarmi la scrittura del documento utilizzerò alcuni termini senza tradurli:

  • Doc Comment: indica un commento all'interno di un file .java utile alla creazione di JavaDoc.
  • JDTool: è un riferimento al tool di creazione dei JavaDoc, il JavaDoc Tool

Un po' di Storia

JavaDoc nacque come strumento interno utilizzato dai ricercatori della Sun che stavano lavorando alla creazione del linguaggio Java e delle sue librerie; la grande mole di sorgenti spinse alcuni membri del team a creare un programma per la generazione automatica di documentazione HTML.

Questo formato infatti consente una navigazione molto efficace e veloce, è molto conosciuto dai programmatori ed è facilmente indicizzabile dai motori di ricerca. Tuttavia, la creazione e manutenzione di una tale mole di pagine web non sarebbe stata pensabile senza l'aiuto di un sistema automatico: basti pensare alla quantità di riferimenti incrociati che ci sono fra le classi (ereditarietà fra classi, firme dei metodi, riferimenti a package solo per citarne alcuni) e agli inevitabili errori di battitura a cui si va incontro scrivendo documentazione.

JavaDoc nacque quindi per permettere ai programmatori di inserire dei frammenti HTML nei commenti (ignorati quindi dal compilatore): già con le prime versioni si potevano inserire le descrizioni di ogni classe e dei suoi metodi, nonché il significato dei parametri e delle variabili membro.

Con il progredire delle versioni JavaDoc diventò sempre più sofisticato e ricco di funzioni:

  • inserimento di link, anche a JavaDoc esterni;
  • inserimento dell'indicazione @deprecated per segnalare classi e/o metodi destinati a scomparire in future versioni del software;
  • opzioni per la formattazione avanzata;
  • possibilità di creare le proprie Doclet: estensioni di JavaDoc che permettono di gestire a piacimento le varie fasi di generazione della documentazione;

Le Doclet in particolare permisero ad altre case produttrici di software e ad altri sviluppatori (soprattutto open source) di creare strumenti molto diversificati:

  • generazione di schemi UML, grafici di dipendenze fra classi e package, analizzatori di codice (molto utilizzati nell'ingegneria del software);
  • generazione di documentazione in formato PDF, Word, RTF, Microsoft Help, LaTeX, ecc.

Il grande successo di JavaDoc è dovuto alla possibilità di poter creare con facilità una documentazione dall'aspetto professionale, del tutto simile a quella ufficiale, anche da parte del principiante, che impara a valorizzare un aspetto spesso sottovalutato della programmazione, cioè la gestione dei documenti relativi ai propri programmi.

I file HTML che vengono generati dalla doclet standard infatti hanno la stessa organizzazione grafica e logica della documentazione che Sun fornisce per le API che essa distribuisce.

Formato di un Doc Comment

Lo strumento per la creazione di documentazione in formato JavaDoc, ossia il Javadoc tool, esegue il parsing dei file .java alla ricerca di speciali Token dai quali leggere le informazioni che desideriamo pubblicare all'interno della nostra documentazione.
Il primo di questi token ad essere cercato è: /** ........ */

A prima vista appare essere un commento multilinea qualunque, ma in realtà ha due asterischi anziché uno nel token di apertura, cioè quanto basta perché qualunque informazione all'interno di questi delimitatori sia processata dal JDTool.

Un doc comment è scritto in HTML e deve precedere le dichiarazioni di classi, proprietà, metodi e costruttori.

Solitamente è composto da 2 aree: una descrizione seguita da delle parti più specifiche marchiate con token particolari che prendono il nome di Tags. Per esempio:

/**
* Restituisce un'immagine che deve essere visualizzata.
* Il parametro url deve essere una {@link URL} 
* in formato assoluto.
*
* @author  Mirko Agrati
* @version %I%, %G%
* @since   1.0
* @param   URL dove risiede l'immagine
* @param   Nome del File Immagine
* @return  l'immagine corrispondente, 
*          null se non esiste alcuna immagine
*/
public Image getImage(String url, String filename){..}

Questo facile esempio serve per introdurre le prime regole per la generazione di JavaDoc corretti. Si può affermare che:

  • tutto quello contenuto tra /** e */ è l'oggetto del processo di generazione dei JavaDoc;
  • ogni riga deve iniziare con un asterisco * (dalla versione 1.4 è possibile ometterli);
  • E' possibile utilizzare il tag {@link NOME_CLASSE} per creare un link alla classe NOME_CLASSE;
  • E' possibile separare i paragrafi con il tag HTML <p>;
  • E' d'obbligo separare la descrizione dalla lista degli altri tag con una riga bianca;
  • la prima occorrenza del carattere @ fa si che il JDTool termina l'elaborazione della descrizione;
  • può esistere un solo blocco descrittivo, da collocare obbligatoriamente prima della lista dei tag;

La risultante documentazione HTML sarà fedele alla formattazione data ai commenti (unico limite è costituito da paragrafi di 80 colonne).

N.B.: Il JDTool tratta la prima frase del commento descrittivo in maniera particolare, in modo da fare apparire subito a fianco del metodo o classe o proprietà. Quindi è molto importante scrivere la prima frase in maniera concisa ma completa. Questa prima frase termina al primo "." seguito da uno spazio bianco.

Formato di un Doc Comment

Seguono una lista di trucchi e convenzioni per la scrittura di descrizioni nei doc comments.

Utilizzare il tag <code> per nomi e parole chiavi:
E' buona norma enfatizzare keywords e nomi nelle descrizioni racchiudendoli all'interno del tag <code>...</code>.
Questo tag può contenere: Java Keywords (ES: null), nomi di package, di interfacce, di classi, di metodi, di proprietà, di argomenti. E' utilizzato anche per esporre codice Java di esempio.

Non eccedere nell'utilizzo in-line del tag @link:
E' buona norma aggiungere links alle API Java utilizzando il tag {@link}, sebbene non sia obbligatorio. A causa della modalità grafica con cui i link vengono visualizzati (sottolineati e colorati) la documentazione risultante potrebbe essere poco chiara al colpo d'occhio, quindi il tag @link dovrebbe essere utilizzato:

  • se si pensa che sia meglio indirizzare l'utente ad approfondire le API utilizzate dalla nostra classe;
  • solo per linkare la prima occorrenza dell'API a cui ci si vuole riferire;
  • solo per collegare API poco conosciute (ES: inutile linkare il pakage java.lang.String);

Omettere le parentesi per i prototipi di metodi e costruttori:
Quando ci si riferisce ad un metodo o costruttore che ha diversi prototipi e vogliamo riferirci particolarmente ad uno di essi, allora si utilizzano le parentesi ed i Tipi degli argomenti.
Se invece il riferimento è a tutti i prototipi del metodo (o costruttore) allora è buona norma, oltre che convenzione, non utilizzare le parentesi.
Per comprendere meglio si pensi al metodo add della classe ArrayList:
essa espone due metodi add: add(Object) e add(int, Object).
Il primo aggiunge un oggetto in coda alla lista, il secondo metodo
offre la possibilità di scegliere la posizione dove collocare l'oggetto.
Quindi se nella documentazione si vuole descrivere come aggiungere un oggetto alla lista si scriverà: add aggiunge un oggetto alla lista.

Utilizzare lo stile descrittivo:
E' convenzione preferire uno stile breve e descrittivo piuttosto che narrativo. Questo perchè è essenziale avere una documentazione breve, concisa e che metta subito in luce gli aspetti chiave dell'oggetto della nostra descrizione.

Usare la terza persona:
E' buona norma scrivere le descrizioni utilizzando la terza persona singolare in quanto dona un effetto descrittivo a differenza della seconda persona singolare che ha sfumature imperative.
ES:

/**
 * add Aggiunge un oggetto (corretto).
 * add Aggiungi un oggetto (sbagliato da evitare).
 */

Descrivere i metodi iniziando con un verbo:
Un metodo descrive un'operazione, un'azione, quindi solitamente la sua descrizione inizia con un verbo qualificante. ES:

/**
 * add Aggiunge un oggetto (corretto).
 * add Il Metodo add aggiunge un oggetto (da evitare).
 */

Omettere il soggetto nelle descrizioni di Classi, interfacce e Proprietà.
ES:

/**
 * Bottone di conferma (corretto).
 *
 * Questa proprietà rappresenta 
 * un bottone di conferma (da evitare).
 */

Convenzioni nell'utilizzo dei Tag

Segue l'ordine di utilizzo dei Tag:

  • @param (solo classi, interfacce, metodi e costruttori);
  • @return (solo metodi);
  • @exception (dalla versione 1.2 esiste anche il sinonimo @throws);
  • @author (solo classi ed interfacce. OBBLIGATORIO);
  • @version (solo classi ed interfacce. OBBLIGATORIO);
  • @see
  • @since
  • @serial
  • @deprecated

Ordinare liste di Tag uguali

Capita molto spesso che metodi abbiano in input molteplici argomenti o che si voglia fare più di un riferimento ad API diverse. Il risultato è che si devono ordinare in modo logico liste di tag uguali.

  • Molteplici tag @author dovrebbero apparire ordinati cronologicamente, con il creatore della classe in cima alla lista.
  • Molteplici tag @param dovrebbero apparire nell'ordine in cui sono dichiarati nel prototipo del metodo o costruttore.
  • Molteplici tag @exception dovrebbero apparire ordinati alfabeticamente in base al Nome delle eccezioni che potrebbero essere sollevate.
  • Molteplici tag @see dovrebbero essere ordinati in base al punto di accesso più vicino della classe, dall'utilizzo meno qualificato al più specializzato.

Quando è richiesto l'utilizzo dei Tag?

Gli unici Tag il cui utilizzo è obbligatoriamente richiesto a prescindere da ogni situazione sono @author e @version.

Tutti gli altri Tag diventano necessari in base alla situazione che si sta documentando.

  • Il Tag @param è richiesto necessariamente per ogni argomento che riceve il metodo o costruttore descritto, anche se la sua descrizione appare ovvia.
  • Il Tag @return è richiesto per ogni metodo che restituisce qualcosa di diverso da void, anche se la descrizione risultasse ridondante.

Descrizione dei singoli Tag

@param:
Deve essere seguito dal nome del parametro (e non dal suo Tipo) e completato con una descrizione del parametro. Per convenzione il primo sostantivo presente nella descrizione deve riferirsi al Tipo di dato rappresentato dal parametro, eccezione fatta per i tipi primitivi (interi, boolean ecc....).
Non deve essere utilizzato nessun carattere di punteggiatura prima della descrizione, in quanto il JDTool aggiunge della punteggiatura durante il processo di creazione dei JavaDocs.
Il nome del parametro deve essere scritto in minuscolo, come il tipo da lui rappresentato, in quanto indica un riferimento ad un oggetto piuttosto che ad una classe. ES:

@param ch carattere da testare
@param x Coordinata X. Misurata in pixel

@return:
Il Tag deve essere omesso solo per i metodi che non restituiscono niente (void) e per i costruttori. Deve essere presente in ogni altra casistica ed è preferibile documentare eventuali casi limite o speciali.

@throws:
Sostituisce @exception e dovrebbe essere utilizzato per documentare ogni eccezione gestita dal codice e per tutte quelle che ragionevolmente il chiamante si prepara a gestire (per esempio un'istanza della classe java.lang.NullPointerException).ES:

/**
 * @throws IOException In caso di errori 
 * sul byte-streaming
 */
public void open() throws IOException{ ............ }

@since:
Dovrebbe essere utilizzato a livello di package, classe o interfaccia.
Non dovrebbe essere utilizzato per i metodi o proprietà, tranne per le successive aggiunte di codice che non erano presenti alla nascita del package, classe o interfaccia.

@deprecated:
La descrizione fornita dovrebbe essere minima, cioè deve giusto notificare che le API in questione non sono da utilizzare ed eventualmente indicare cosa utilizzare in sostituzione tramite il Tag {@link}. Se non vi sono API in sostituzione, la descrizione dovrebbe indicare solo qualcosa del tipo "Non Ancora Rimpiazzate".

Documentare i costruttori di default

Le regole della Buona Programmazione prevedono che non si dovrebbe mai fare uso di costruttori public di default: tutti i costruttori dovrebbero essere espliciti.
Questo perchè solo creando prototipi di costruttori espliciti avremo la possibilità di documentarli e prima ancora, rendendo esplicita la dichiarazione di un costruttore, si impedirebbe un istanziamento non corretto ed utile della classe.

Commenti a livello di package

Con l'avvento di JavaDoc 1.2 è possibile inserire doc comments anche a livello di package. Ogni package può avere il suo file di commento che il JDTool fonderà insieme alla documentazione durante il processo di creazione dei JavaDoc.

Il file per documentare i package deve chiamarsi package.html.
Il nome non cambia mai, rimane lo stesso per ogni package e deve essere posizionato nella cartella madre del package.

Contenuto del file package.html
Questo file dovrebbe fornire tutte le informazioni necessarie per facilitare i programmatori nell'uso del package. Questo file può essere molto importante perchè è il primo punto di accesso alla documentazione per tutti gli utilizzatori delle API.

Lo scopo fondamentale dell'esistenza di questo file è quello di descrivere
i propositi ed i compiti che il package si propone di svolgere, quindi dovrebbe contenere una breve e chiara descrizione delle potenzialità offerte.

Il seguente è il codice sorgente del template che in Sun utilizzano per la creazione dei commenti a livello di package:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<!-- @(#)package.html 1.60 98/01/27
CopyrightVersion 1.2 -->
</head>
<body bgcolor="white">
# THIS IS THE TEMPLATE FOR THE PACKAGE DOC COMMENTS. #
# TYPE YOUR PACKAGE COMMENTS HERE. BEGIN WITH A #
# ONE-SENTENCE SUMMARY STARTING WITH A VERB LIKE: #
Provides for....
<h2>Package Specification</h2>
# FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT #
<ul>
<li>
<a href="">
  # REFER TO ANY FRAMEMAKER SPECIFICATION HERE #
</a></li>
</ul>
<h2>Related Documentation</h2>
For overviews, tutorials, examples, guides, 
and tool documentation, please see:
<ul>
<li>
  <a href="">
    # REFER TO NON-SPEC DOCUMENTATION HERE #
  </a></li></ul>
<!-- Put @see and @since tags down here. -->
</body>
</html>

Come si può notare, il Template contiene due sezioni specifiche oltre ad una breve descrizione introduttiva.

La prima sezione, chiamata Package Specification, dovrebbe includere specifiche descrizioni e chiarimenti che non sono presenti nei restanti JavaDoc creati ed eventuali riferimenti a tutte quelle specifiche descritte in documenti esterni.

La seconda sezione, chiamata Related Documentation, può includere riferimenti e/o link a documentazione esterna tipo Tutorial, guide o esempi, materiale che comunque non contiene specifiche ma che può completare la documentazione a supporto degli sviluppatori.

Documentare la classi anonime

Attualmente il JDTool non documenta le classi anonime che incontra nel processo di scansione del codice sorgente, o meglio ignora completamente i doc comments. Quindi, se si vuole fornire una documentazione per queste classi, si è costretti ad aggiungerla alla classe o metodo esterno che le contiene.
Per esempio, se all'interno di un metodo dichiariamo una classe anonima, per documentarla dovremo fornirne una descrizione assieme a quella del metodo
che la contiene.

Un esempio completo di Doc Comments

/**
 * Graphics is the abstract base class for all 
 * graphics contexts which allow an application 
 * to draw onto components realized on various 
 *devices or onto off-screen images.
 * A Graphics object encapsulates the state 
 * information needed for the various rendering 
 * operations that Java supports. 
 * This state information includes:
 * <ul>
 * <li>The Component to draw on</li>
 * <li>
 *   A translation origin for rendering and 
 *   clipping coordinates</li>
 * <li>The current clip</li>
 * <li>The current color</li>
 * <li>The current font</li>
 * <li>The current logical pixel operation 
 *   function (XOR or Paint)</li>
 * <li>The current XOR alternation color
 * (see <a href="#setXORMode">setXORMode</a>)</li>
 * </ul>
 * <p>
 * Coordinates are infinitely thin and lie between 
 * the pixels of the output device.
 * Operations which draw the outline of a figure 
 * operate by traversing along the infinitely thin 
 * path with a pixel-sized pen that hangs down and 
 * to the right of the anchor point on the path.
 * Operations which fill a figure operate by filling 
 * the interior of the infinitely thin path.
 * Operations which render horizontal text render 
 * the ascending portion of the characters entirely 
 * above the baseline coordinate.
 * <p>
 * Some important points to consider are that drawing 
 * a figure that covers a given rectangle will occupy 
 * one extra row of pixels on the right and bottom edges 
 * compared to filling a figure that is bounded by that 
 * same rectangle.
 * Also, drawing a horizontal line along the same y 
 * coordinate as the baseline of a line of text will 
 * draw the line entirely below the text except for 
 * any descenders.
 * Both of these properties are due to the pen hanging 
 * down and to the right from the path that it traverses.
 * <p>
 * All coordinates which appear as arguments to the 
 * methods of this Graphics object are considered relative 
 * to the translation origin of this Graphics object prior 
 * to the invocation of the method.
 * All rendering operations modify only pixels which lie 
 * within the
 * area bounded by both the current clip of the graphics 
 * context and the extents of the Component used to create 
 * the Graphics object.
 *
 * @author Sami Shaio
 * @author Arthur van Hoff
 * @version %I%, %G%
 * @since 1.0
 */
public abstract class Graphics {
 /**
  * Draws as much of the specified image as is currently 
  * available with its northwest corner at the specified 
  * coordinate (x, y).
  * This method will return immediately in all cases, 
  * even if the entire image has not yet been scaled, 
  * dithered and converted for the current output device.
  * <p>
  * If the current output representation is not yet 
  * complete then the method will return false and the 
  * indicated {@link ImageObserver} object will be notified 
  * as the conversion process progresses.
  *
  * @param img the image to be drawn
  * @param x the x-coordinate of the northwest corner
  * of the destination rectangle in pixels
  * @param y the y-coordinate of the northwest corner
  * of the destination rectangle in pixels
  * @param observer the image observer to be notified as 
  * more of the image is converted. May be
  * <code>null</code>
  * @return <code>true</code> if the image is 
  * completely loaded and was painted successfully;
  * <code>false</code> otherwise.
  * @see Image
  * @see ImageObserver
  * @since 1.0
  */
 public abstract boolean drawImage(Image img, int x, int y,
   ImageObserver observer);
 /**
  * Dispose of the system resources used by this graphics 
  * context. The Graphics context cannot be used after 
  * being disposed of. 
  * While the finalization process of the garbage 
  * collector will also dispose of the same system 
  * resources, due to the number of Graphics objects that 
  * can be created in short time frames it is preferable 
  * to manually free the associated resources using this 
  * method rather than to rely on a finalization process 
  * which may not happen for a long period of time.
  * <p>
  * Graphics objects which are provided as arguments to 
  * the paint and update methods of Components are 
  * automatically disposed by the system when those methods 
  * return. 
  * Programmers should, for efficiency, call the dispose 
  * method when finished using a Graphics object only if 
  * it was created directly from a Component or another 
  * Graphics object.
  *
  * @see #create(int, int, int, int)
  * @see #finalize()
  * @see Component#getGraphics()
  * @see Component#paint(Graphics)
  * @see Component#update(Graphics)
  * @since 1.0
  */
 public abstract void dispose();
 /**
  * Disposes of this graphics context once it is no 
  * longer referenced.
  *
  * @see #dispose()
  * @since 1.0
  */
 public void finalize() {
  dispose();
 }
}

Personalmente utilizzo spesso la sintassi JavaDoc per documentare classi e metodi dei miei progetti Java e J2EE, sicuramente però non la curo in modo così preciso e minuzioso come sopra descritto.

Alla prossima,
MA

venerdì 19 marzo 2010

Javascript: La Programmazione Orientata agli Oggetti - OOP

Con l'avvento del Web2.0 e quindi con l'esponenziale arricchimento delle funzionalità delle interfacce grafiche e dei processi che le governano,
è divenuto essenziale per il programmatore riorganizzare il codice client in maniera che sia più facile da mantenere ed implementare.

Grazie ad alcuni frameworks ormai famosi, tipo Prototypejs e JQuery, è stata migliorata la produttività del programmatore, che finalmente può scrivere codice senza preoccuparsi (nella maggior parte dei casi) delle differenti piattaforme sul quale poi dovrà funzionare, concentrandosi maggiormente sull'organizzazione e sull'architettura dell'applicazione.

JavaScript permette un approccio alla programmazione sia strutturata, o procedurale, sia Object Oriented, o OOP.

Personalmente ritengo che organizzare in classi la gestione dello stato e dei dati applicativi sia alla lunga più conveniente: la possibilità di assemblare tutta la logica di funzionamento e comportamento all'interno di ogni singolo mattone che compone l'applicazione rende tutto il ciclo di vita dell'applicativo (analisi, realizzazione, test, rilascio e successive implementazioni) più fluido e più stabile perché logicamente organizzato in maniera migliore.

Certamente non è proprio corretto parlare di OOP in quanto JavaScript non ha i concetti di classe e di interfaccia, tanto meno di polimorfismo, conosce appena il senso di privato e pubblico e non è un linguaggio fortemente tipizzato.

Javascript pur non essendo dotato di tutte queste caratteristiche e funzionalità, permette comunque la creazione di oggetti: questo ed altre particolarità del linguaggio (tipo il metodo prototype) ci offrono la possibilità di colmare
le mancanze del linguaggio estendendone le funzionalità ed i concetti.

Definire ed Istanziare un Oggetto

Per istanziare un Oggetto si utilizza l'operatore new seguito dal costruttore della classe:

<script type="text/javascript">
<!--
//Esempio di dichiarazione di Classe.
/**
 * La classe Player ha 3 proprieta': 
 * Nome,Cognome e Sesso. 
 */
function Player(nm, cgn, ss){
  this.nome = nm;
  this.cognome = cgn;
  this.sesso = ss;
}

/**
 * Estendo le funzionalità di tutte 
 * le istanze della classe con il metodo 
 * toString().
 */
Player.prototype.toString() = function(){
  return this.nome + " " + this.cognome 
     + " (" + this.sesso + ")"; 
}

//Ora istanzio un oggetto di tipo Player.
var player = new Player('Mirko', 'Agrati', 'UOMO');

alert(player.toString());
-->  
</script>

Come si nota, creare una classe ed istanziare un oggetto è cosa da poco. Forse, meno intuitivo è l'utilizzo dell'operatore this.

this rappresenta un riferimento al contenitore padre(classe, metodo od oggetto che sia), che grazie al proprio reference si espone offrendo la possibilità di essere esteso in qualunque momento o stato applicativo.

La classe Player, per come è stata creata, offre un diretto controllo delle sue proprietà permettendo all'utente di modificarne i valori in qualunque momento.

Ma se invece volessimo progettare un oggetto che una volta istanziato proteggesse le sue proprietà da possibili modifiche?

Private & Public

Come detto in precedenza, JavaScript non offre i concetti di scope public o private però il concetto generale di scope lo conosce bene, infatti una variabile dichiarata all'interno di una funzione non viene vista al suo esterno.

Sfruttando questo meccanismo, basterà utilizzare delle variabili interne, per ottenere il mascheramento delle proprietà, e dei metodi pubblici per potervi accedere.

<script type="text/javascript">
<!--
function Player(nm, cgn, ss){
  nome = nm;
  cognome = cgn;
  sesso = ss;
 
  /**
   * Dichiaro i metodi pubblici per esporre 
   * i valori delle proprietà
   */
  this.getNome = function(){
    return nome;
  }
  this.getCognome = function(){
    return cognome;
  }
  this.getSesso = function(){
    return sesso;
  }
}

//Istanzio un oggetto di tipo Player.
var player = 
    new Player('Mirko', 'Agrati', 'UOMO');
alert(player.getNome());

/**
 * Per verificare che dall'esterno 
 * le proprietà sono invisibili
 * provo ad accedervi direttamente. 
 * Mi aspetto di ricevere un valore 
 * NULL o NaN.
 */
alert(player.nome);
-->  
</script>

Quindi è possibile gestire scope di membri come fossero variabili e perciò si può dire che il concetto base dell'incapsulamento, caratteristica fondamentale della OOP (insieme all'ereditarietà ed il polimorfismo) è supportato dal linguaggio.

Dopo l'ultima modifica, la classe Player si è parecchio irrobustita per quanto riguarda la sicurezza dei propri dati, ma ancora un punto debole esiste.

Il costruttore infatti accetta come terzo parametro il sesso, passato come Stringa: anche se esistono due soli sessi questi possono essere scritti in molteplici modi differenti, maiuscoli e minuscoli o in lingue differenti ecc....

Come reagirebbe la classe se anziché "UOMO" le passassi "OMMO" o "MAN"?
Per evitare questi problemi, in ambito OOP solitamente si utilizzano delle costanti static, ma JavaScript cosa offre?

Static

Bhe, l'utilizzo di costanti è da accantonare, mentre per quanto riguarda proprietà e membri static JavaScript offre una possibilità.

Cos'è un membro static?
Un membro static è un metodo, o proprietà, il cui scope, cioè la sua visibilità, è definito a livello di classe, cioè è utilizzabile senza dover creare alcuna istanza di classe.

Quindi vediamo come modificare il codice della classe Player introducendo la definizione di un membro static ed il suo utilizzo.

<script type="text/javascript">
<!--
//Static
Player.SEX_MALE = 1;
Player.SEX_FEMALE = 2;

//Esempio di dichiarazione di Classe.
function Player(nm, cgn, ss){
  if(ss != Player.SEX_MALE 
      && ss != Player.SEX_FEMALE ){
    alert('Impossibile istanziare un oggetto '
        + 'della classe Player');
    return null;
  }
 
  nome = nm;
  cognome = cgn;
  sesso = ss;
 
  this.getNome = function(){
    return nome;
  }
  this.getCognome = function(){
    return cognome;
  }
  this.getSesso = function(){
    return sesso;
  }
}

//Istanzio un oggetto di tipo Player.
var player = 
  new Player('Mirko', 'Agrati', Player.SEX_MALE);

alert(player.getNome());

/**
 * Per verificare che il meccanismo 
 * di controllo funzioni correttamente
 * provo a passare come sesso un valore 
 * non contemplato.
 * Mi aspetto un alert bloccante di avviso.
 */ 
player = new Player('Mirko', 'Agrati', 'UOMO');
-->  
</script>

E' bello sapere di poter contare su una caratteristica per me fondamentale della tecnica di programmazione come i membri static!!!

Ok, ora la classe Player è proprio barricata e non le manca nulla!
Darà sicuramente vita a buoni oggetti Player!

La OOP si fonda anche sull'ereditarietà oltre che sul polimorfismo e JavaScript non permette direttamente di ereditare da una classe. ;(

Però grazie ai membri prototype e call() possiamo indirettamente creare un meccanismo per colmare la mancanza dell'ereditarietà. Come?

Prototype

Ogni funzione Javascript possiede un attributo prototype che incapsula un oggetto di tipo Prototype.

All'oggetto prototype possiamo aggiungere attributi e metodi come potremmo fare con qualsiasi altro oggetto ma, differentemente da tutti gli altri oggetti, estendendo l'oggetto prototype in realtà si estendono tutte le istanze della
classe proprietaria dell'oggetto prototype su cui si sta agendo.

Questo perchè lo scopo esistenziale dell'oggetto prototype è fare da modello
(prototipo appunto) per la creazione di altri oggetti e modificarlo implica la creazione di un nuovo constructor di tale oggetto.

Ereditarietà

Ora mostrerò come poter derivare dalla classe base Player un nuovo tipo Calciatore.

Per fare questo utilizzerò il metodo call() e la proprietà constructor che ogni oggetto JavaScript mette a disposizione.

Il metodo call() permette di condividere un oggetto tra più funzioni, od oggetti, semplicemente chiamando da una funzione il costruttore di un'altra e passando this come parametro.

La proprietà constructor invece racchiude il costruttore della nuova classe: è necessario utilizzarla perché nel momento in cui si fa una chiamata con il metodo call() il membro prototype dell'oggetto chiamante viene sovrascritto con quello dell'oggetto chiamato.

<script type="text/javascript">
<!--
//Static
Player.SEX_MALE = 1;
Player.SEX_FEMALE = 2;

Calciatore.ATTACCANTE = "Attaccante";
Calciatore.DIFENSORE = "Difensore";
Calciatore.CENTROCAMPISTA = "CentroCampista";
Calciatore.PORTIERE = "Portiere";

function Player(nm, cgn, ss){
  if(ss != Player.SEX_MALE 
      && ss != Player.SEX_FEMALE ){
    alert('Immpossibile instanziare un oggetto '
        + ' della classe Player');
    return null;
  }
 
  nome = nm;
  cognome = cgn;
  sesso = ss;
 
  this.getNome = function(){ return nome;}
  this.getCognome = function(){ return cognome; }
  this.getSesso = function(){ return sesso; }
}

/**
 * La classe Calciatore ha 6 proprietà: 
 * Nome, Cognome, Sesso, Ruolo, Titolare e nMaglia.
 */
function Calciatore(n, c, s, r, t, m){
  Player.call(this,n,c,s);
  this.prototype = new Player(n,c,s);
  this.prototype.constructor = Calciatore;
  ruolo = r;
  titolare = t;
  nMaglia = m;
 
  this.getRuolo = function(){ return ruolo; }
  this.isTitolare = function(){ return titolare; }
  this.getNMaglia = function(){ return nMaglia; }
}

//Ora istanzio un oggetto di tipo Calciatore.
var mirko = 
    new Calciatore('Mirko', 'Agrati', Player.SEX_MALE
        , Calciatore.ATTACCANTE, true, 23);

  alert(mirko.getNMaglia() + ' - ' 
      + mirko.getNome() + ' ' 
      + mirko.getCognome() + ' - ' 
      + mirko.getRuolo());
-->  
</script>

Ecco fatto! Se si esegue il codice di esempio si potrà verificare che la classe Calciatore possiederà anche le proprietà ed i metodi della classe Player, da cui deriva.

Per quanto riguarda il polimorfismo si deve usare un processo più complicato ma efficace che consiste nell'estendere le proprietà baseConstructor e superClass.

Certamente rimane il limite delle 2 sole classi possibili da derivare, ma per essere un linguaggio di scripting JavaScript non si fa mancare niente.

Opinioni personali

E' possibile abbandonare la programmazione strutturata in favore della OOP anche per quanto riguarda la parte client delle applicazioni web, sfruttando anche le
potenzialità della sintassi JSON si possono ottenere risultati veramente soddisfacenti e sicuramente più facili da mantenere ed implementare.

Alla prossima,
MA.

XSD: Definire elementi Simple Type

In questo articolo riguardante il capitolo XSD cercherò di chiarire cos'è un elemento Simple Type e come è strutturato in modo tale da saperne fornire la dichiarazione XSD.

A volte, definire un tipo di dati di un elemento come 'stringa' o 'intero' potrebbe non essere sufficiente: infatti potremmo voler restringere ad un determinato range, per esempio, i valori numerici di un elemento XSD.

Tutte queste necessità possono essere definite all'interno di un XSD_Schema tramite il linguaggio XSD.

I Simple Type elements comprendono le seguenti categorie:

  • elementi semplici;
  • attributi;
  • restrizioni o facets;

Simple Type

Un Simple Type è un elemento XML, privo di attributi, che contiene solo testo (quindi totale assenza di ulteriori nodi figlio o child nodes).

La sintassi di base per la definizione di un Simple Type e':

<xs:element name="xxx" type="xxx" />

L'attributo name rappresenta il nome dell'elemento, mentre type indica il tipo dei dati in esso contenuti.

Il linguaggio XSD dispone di diversi tipi di dati predefiniti o build in, i più comunemente utilizzati sono:

Tipi di dati Predefiniti
TipoDescrizione
stringStringhe di caratteri
decimalValori numerici generici
integerValori numerici interi
booleanValori booleani: true o false
dateDate
timeOrari

Quindi, per esempio, potremo definire un Simple Type in questa maniera:

<xs:element name="nome" type="xs:string" />

Che corrisponderebbe ad un elemento XML di questo tipo:

<nome>Mirko</nome>

Gli elementi semplici possono avere un valore di default che viene assegnato in mancanza di quello dato:

<xs:element name="nome" 
    default="Senza Nome" type="xs:string" />

Oppure possono avere un valore fixed che viene assegnato sempre.

Da ricordare che fixed e default si escludono a vicenda:

<xs:element name="home" 
       fixed="http://mirkoagrati.blogspot.com" 
       type="xs:string" />

Gli Attributi

Se un elemento contiene attributi, per definizione, diventerà necessariamente un Complex Type o Tipo complesso.

Tuttavia gli attributi XSD rientrano nella sfera dei Simple Type: infatti la sintassi per definire un attributo ha molte similitudini con quella per definire un elemento semplice:

<xs:attribute name="xxx" type="xxx" />

E come gli elementi semplici, anche gli attributi possono avere la definizione di fixed o default.

In aggiunta, gli attributi definiti dallo schema, per natura, sono opzionali ovvero anche se presenti nello schema_XSD possono risultare mancanti nel documento XML, a meno che non siano dichiarati con l'attributo use impostato a require: questo é l'unico modo per rendere un attributo obbligatorio.
Per esempio:

<xs:attribute name="id" 
    use="require" type="xs:string" />

Restrizioni o Facets

A volte potremmo voler restringere ad un determinato range, per esempio, i valori numerici di un elemento XSD.

E' per questo che ai tipi_di_base_XSD possono essere applicate ulteriori restrizioni o facets.

Per esempio:
Vorremmo poter esprimere un elemento stringa che non contenga più di 20 caratteri.

Come fare?
Grazie a questa feature potremmo validare una stringa in questa maniera:

<xs:element name="cognome"> 
      <xs:simpleType> 
      
         <xs:restriction base="xs:string"> 
            <xs:minLength value="0"/> 
            <xs:maxLength value="20"/> 
         </xs:restriction> 
      
      </xs:simpleType> 
   </xs:element>

Ovviamente, se fosse necessario utilizzare una facet in più elementi conviene allora definirla esternamente, in modo da condividerla con tutti gli elementi e attributi che devono utilizzarla:

<xs:simpleType name="limitedStr"> 
      <xs:restriction base="xs:string"> 
         <xs:minLength value="0"/> 
         <xs:maxLength value="20"/> 
      </xs:restriction> 
   </xs:simpleType>

A questo punto basta assegnare all'attributo type di ogni elemento che necessita di limitazioni il nuovo tipo limitedStr:

<xs:element name="cognome" type="limitedStr" />

La tabella seguente riassume i tipi di restrizioni che il linguaggio XSD fornisce:

Facets Utilizzabili
Restrizione Descrizione
enumeration Lista di valori
fractionDigits Numero massimo di decimali
length Numero esatto di caratteri
maxExclusive Il valore numerico deve essere inferiore a quello specificato
maxInclusive Il valore numerico deve essere inferiore o uguale a quello specificato
maxLength Numero massimo di caratteri ammessi
minExclusive Il valore numerico deve essere superiore a quello specificato
minInclusive Il valore numerico deve essere superiore o uguale a quello specificato
minLength Numero minimo di caratteri ammessi
pattern Sequenza di caratteri ammessi (regular expressions)
totalDigits Numero di cifre ammesse
whiteSpace Specifica come gestire gli spazi bianchi

Ciò che è stato esposto in questo articolo risulta essere sicuramente importante per una definizione accurata delle tipologie di dati complesse che si vorranno creare a partire dalla loro definizione XSD.

Alla prossima,
MA.

martedì 16 marzo 2010

Javascript: Utilizzare gli Array Associativi

In questo articolo proporrò alcuni script javascript per documentare una tecnica di utilizzo degli Array Associativi che adopero spesso e che ritengo molto valida per evitare ogni volta di scorrere un array per trovare l'oggetto cercato.

Cos'è un Array Associativo?

Con il termine Associativo si fa riferimento alla possibilità di poter abbinare un oggetto, o un valore da conservare, con un token o chiave che non sia necessariamente un numero, solitamente l'indice del array.

Esempio:

/* Utililzzo tradizionale per indice */
var arr = new Array();
arr[0] = 'xxxx';
arr[1] = 'tttt';
arr[2] = 'aaaa';

/* Utililzzo per associazione */
var arr = new Array();
arr['x'] = 'xxxx';
arr['t'] = 'tttt';
arr['a'] = 'aaaa';

Chi sviluppa in Java utilizza per questo genere di soluzioni un'istanza della classe java.util.Map.

Perchè e quando usarli?

L'esempio concreto che descriverò in questo articolo riguarda proprio il modo con cui ho affrontato e risolto un problema nato durante l'analisi del progetto di rifacimento di un sito.

Una delle specifiche assegnate era la totale separazione dei contenuti dal modo in cui essi sarebbero stati presentati nel sito.

Scelsi quindi di utilizzare XML come fonte dati e javascript per la realizzazione dell'interfaccia client.

Nacque quindi il problema di come poter individuare e trasformare in link solo determinate parole mischiate in tutto il contenuto da mostrare.

L'idea che ho avuto è stata di parsare ogni singola parola del documento al
caricamento della pagina e confrontarla con una serie di elementi contenuti in un array per eventualmente trasformare la parola letta in un link HTML.

Inizialmente il mio array era stato creato tradizionalmente, in questo modo:

var link = new Array();

link[0] = {'key':'java','value':'http://java.sun.com/reference/api/'};
link[1] = {'key':'xml','value':'http://www.w3schools.com/xml/'};
link[2] = {'key':'http://java.sun.com/j2se/javadoc/writingdoccomments/'
    ,'value':'http://java.sun.com/j2se/javadoc/writingdoccomments/'};
link[3] = {'key':'sun','value':'http://www.sun.com'};
link[4] = {'key':'j2ee','value':'http://java.sun.com/javaee/'};
link[5] = {'key':'j2me','value':'http://java.sun.com/javame/'};
link[6] = {'key':'jme','value':'http://java.sun.com/javame/'};
link[7] = {'key':'struts','value':'http://struts.apache.org/'};
link[8] = {'key':'poi','value':'http://poi.apache.org/'};

E il confronto lo eseguivo con una funzione simile a questa:

function confronta(parola){
  for(var i=0; i<link.length; i++){
    if(parola.toUpperCase() == link[i].key.toUpperCase())
      return '<a href="' + link[i].value + '">' + parola + '<a>';
  }
  return parola;

Fino a che l'array era composto di pochi elementi, diciamo una cinquantina di elementi, scorrerlo e confrontare ogni parola del documento XML con la proprietà key di ogni suo elemento non sembrava pesare più di tanto, ma con il crescere del numero di informazioni da conservare e dei termini da confrontare il procedimento era diventato molto pesante: IE aveva dei tempi di risposta veramente insopportabili! :(

Dovevo trovare una soluzione migliore.
Così introdussi l'utilizzo degli array associativi ottenendo una risposta a livello di performance veramente eccezionale ed imprevista: il risultato del procedimento di ricerca e trasformazione in link HTML era presso che immediato e le risorse impegnate sul client sono diminuite di circa il 69% rispetto alla soluzione precedentemente utilizzata.

Quindi il mio array diventò:

var link = new Array();

link['java'] = 'http://java.sun.com/reference/api/';
link['xml'] = 'http://www.w3schools.com/xml/';
link['http://java.sun.com/j2se/javadoc/writingdoccomments/'] 
    = 'http://java.sun.com/j2se/javadoc/writingdoccomments/';
link['sun'] = 'http://www.sun.com';
link['j2ee'] = 'http://java.sun.com/javaee/';
link['j2me'] = 'http://java.sun.com/javame/';
link['jme'] = 'http://java.sun.com/javame/';
link['struts'] = 'http://struts.apache.org/';
link['poi'] = 'http://poi.apache.org/';

Utilizzando questa tipologia di struttura dati il procedimento per eseguire il confronto si è ridotto ad una semplice funzione simile a questa:

function confronta(parola){
  var t = link[parola.toLowerCase() + ''];
  return (t != null && t.length > 0) 
    ? '<a href="' + t + '">' + parola + '</a<' 
    : parola;
}

Conclusioni

Cosa dire?
Non esiste proprio paragone! :)

La differenza è abissale: nel primo approccio, ogni parola del documento doveva essere confrontata con ogni elemento dell'array fino a quando non si verificava il match tra i termini, e siccome per la maggior parte delle parole il match non avveniva, immagina quanti giri a vuoto e risorse sprecate ogni volta produceva la scansione !!!

Differentemente nel secondo approccio, potendo sfruttare una corrispondenza univoca tra la parola da trasformare in link e la chiave di accesso all'array, viene evitata completamente la ricerca all'interno dell'array riducendo notevolmente tempi di attesa e risorse impegnate.

Consiglio di valutare l'ipotesi di utilizzare questa tecnica ogni volta che sia possibile.

Alla prossima,
MA.

sabato 13 marzo 2010

XSLT: L'istruzione xsl:template

In questo articolo mostrerò come poter utilizzare l'istruzione XSLT xsl:template, all'interno di un foglio di stile, per trasformare un documento XML in una pagina XHTML nel momento in cui viene ricevuto dal browser, cioè appena prima che il risultato del processo di trasformazione XSLT venga presentato all'utente.


Affidarsi al server o al client?

Sempre più spesso è necessario elaborare e presentare dati contenuti in documenti XML. Fortunatamente, esistono componenti_software e tecniche applicabili facilmente sia lato server sia lato client per raggiungere tale scopo.

L'utilizzo di XSLT a mio avviso è specifico del lato client: poiché i più recenti browsers supportano questo standard, non trovo motivo alcuno di dover garantire la compatibilità cross-browser del risultato lavorando il documento XML lato server e delegando il compito della trasformazione XSLT al client si risparmiano risorse sul web server.

Un'alternativa all'utilizzo di XSLT é la trasformazione di documenti XML tramite l'oggetto Javascript XMLHttpRequest(), cuore di qualunque RIA (abbreviazione di Rich Internet Application) e del noto rivoluzionario Web2.0.


I dati XML

Per creare la base dati XML mi sono ispirato al giardinaggio, immaginando alcuni possibili oggetti e piante contenuti in una piccola serra.

Dunque, partiamo con il documento XML:

<?xml version="1.0" encoding="UTF-8"?>

<!-- Aggancio il foglio di style xsl 
    che genererà la parte grafica -->
<?xml-stylesheet 
    type="text/xsl" href="Template.xsl"?>

<!-- Una possibile serra -->
<serra>
  <pianta>
    <nome>Geranio</nome>
    <esterno>true</esterno>
    <colore>rosso</colore>
    <color>red</color>
  </pianta>
  <pianta>
    <nome>Rosa</nome>
    <esterno>true</esterno>
    <colore>arancione</colore>
    <color>orange</color>
  </pianta>
  <pianta>
    <nome>celosia</nome>
    <esterno>true</esterno>
    <colore>viola</colore>
    <color>violet</color>
  </pianta>
  <pianta>
    <nome>felce</nome>
    <esterno>false</esterno>
    <colore>verde scuro</colore>
    <color>darkgreen</color>
  </pianta>
  <vaso>
    <diametro>22 cm</diametro>
    <desc>piccolo</desc>
    <util>Utilizzato per piccole piante 
        o fiori (viola, cactus)</util>
    <color>brown</color>
  </vaso>
  <vaso>
    <diametro>44 cm</diametro>
    <desc>medio</desc>
    <util>Utilizzato per piante o fiori 
        (rosa, ficus) di medie dimensioni</util>
    <color>silver</color>
  </vaso>
  <attrezzo>
    <nome>Zappa</nome>
    <dimensioni>30x10 cm</dimensioni>
    <materiale>acciaio</materiale>
    <util>Utilizzato per fioriere di medie 
        dimensioni e piccole profondita'</util>
    <color>silver</color>
  </attrezzo>
</serra>

Dopo aver visto il documento XML dell'esempio, sul quale non c'è niente da dire né commentare, passiamo alla parte più interessante dell'articolo, ovvero il processo di trasformazione XSLT dei dati XML.

Creazione ed Utilizzo di Template XSLT

Per iterare agilmente, all'interno di un documento XML, su tutti i child nodes di un nodo padre (nel nostro caso la root del documento) ed applicare ad ognuno il corrispettivo template, che si occuperà di trasformare il contenuto XML del nodo in puro XHTML per il browser, farò uso del selettore XPath '*' per valorizzare l'attributo match dell'istruzione <xsl:apply-templates match="nome del nodo" />.


Nel foglio di stile XSLT che ho creato è presente un commento per ogni linea di codice che contiene istruzioni o dichiarazioni di rilevante importanza, quindi basta leggere attentamente il codice sotto riportato per poter capire il funzionamento e l'utilizzo dell'istruzione xsl:template e non solo.

La rappresentazione grafica è scarna volutamente per non offuscare la parte di elaborazione XSLT.

<?xml version="1.0" encoding="UTF-8"?>

<!-- dichiaro che tipo di documento 
    il browser deve interpretare -->
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- scelgo di iniziare l'elaborazione 
    del documento XML a partire dal nodo serra -->
<xsl:template match="/serra">

<html>
<head>
  <title>Serra</title>
  <style>
    div{width: 50%; text-align:left;}
    table{width: 1%;color: darkblue;}
    td{white-space: nowrap;width: 1%;}
  </style>
</head>
<body>
  <center>

  <!-- per ogni nodo figlio di serra 
      applico il rispettivo template -->
  <xsl:apply-templates select="*"/>

  </center>
</body>
</html>

</xsl:template>

<!-- template per la rappresentazione 
    grafica di ogni nodo pianta -->
<xsl:template match="pianta">

<!-- utilizzo di una variabile: 
    é il valore del nodo color, 
    figlio del nodo pianta -->
<div style="background-color: {color}">
  <table>
    <tr> 
      <th colspan="2"> Pianta </th>
    </tr>
    <tr>
      <td> Nome : </td>
      <td> 
        <!-- Estraggo il valore del tag nome -->
        <xsl:value-of select="nome" /> 
      </td>
    </tr>
    <tr>
      <td> Da Esterno : </td>
      <!-- il costrutto XSLT choose 
          e' come switch di Java -->
      <xsl:choose>
        <!-- l'istruzione XSLT when 
            e' come il case di Java -->
        <xsl:when test="esterno = 'true'">
           <td> Si </td>
        </xsl:when>

        <!-- otherwise viene utilizzato 
            se non viene trovata nessuna condizione 
            vera di when.E' il default del switch Java -->
        <xsl:otherwise>
          <td> No </td>
        </xsl:otherwise>
      </xsl:choose>
    </tr>
    <tr>
      <td> Colore : </td>
      <td> <xsl:value-of select="colore" /> </td>
    </tr>
  </table>
</div>
<br />
</xsl:template>

<!-- template per la rappresentazione 
    grafica di ogni nodo vaso -->
<xsl:template match="vaso">
<div style="background-color: {color}">
  <table>
    <tr> <
      th colspan="2"> Vaso </th> 
    </tr>
    <tr>
      <td> Diametro : </td>
      <td> 
        <xsl:value-of select="diametro" />
      </td>
    </tr>
    <tr>
      <td> Descrizione : </td>
      <td>
        <xsl:value-of select="desc" />
      </td>
    </tr>
    <tr>
      <td> Nota : </td>
      <td>
        <xsl:value-of select="util" />
      </td>
    </tr>
  </table>
</div>
<br />
</xsl:template>

<!-- template per la rappresentazione 
    grafica di ogni nodo attrezzo -->
<xsl:template match="attrezzo">
<div style="background-color: {color}">
  <table>
    <tr>
      <th colspan="2"> 
        <xsl:value-of select="nome" />
      </th>
    </tr>
    <tr>
      <td> Dimensioni : </td>
      <td>
        <xsl:value-of select="dimensioni" />
      </td>
    </tr>
    <tr>
      <td> Materiale : </td>
      <td>
        <xsl:value-of select="materiale" />
      </td>
    </tr>
    <tr>
      <td> Nota : </td>
      <td> 
        <xsl:value-of select="util" /> 
      </td>
    </tr>
  </table>
</div>
<br />
</xsl:template>

</xsl:stylesheet>

Per poter testare l'esempio, è necessario creare un file XML di nome Template.xml contenente il codice XML presentato nella 1a parte dell'articolo, poi, sempre nella stessa cartella, è da creare un file XSLT di nome Template.xsl che contiene il codice XSLT riportato sopra; aprendo il file Template.xml con il browser avverrà magicamente la trasformazione XSLT.

Alla prossima,
MA.

martedì 9 marzo 2010

Javascript: Filtrare Testi e Trasformare Parole

In questo articolo mostrerò come poter filtrare porzioni di testo per poi analizzare, ed eventualmente trasformare in link HTML, ogni singola parola incontrata.

Ogni termine analizzato verrà utilizzato come chiave di accesso ad un array_associativo contenente una serie di URLs, ognuna abbinata alla relativa key, per poter eseguire nel testo originale la sostituzione delle parole (e chiavi) con il link HTML alla URL recuperata dall'array.

Per fare questo creerò due classi Javascript, farò uso di un array associativo e del framework JQuery.

Separazione dei contenuti dall'interfaccia Web

Un sistema come quello che sto per mostrare contribuisce alla realizzazione della separazione dei dati, cioè i contenuti da mostrare, dall'esigenze e dalle caratteristiche dell'interfaccia grafica implementata, secondo le regole esposte dal design_pattern MVC (Model-View-Controller).

Nel mio caso, utilizzo questa soluzione per evitare di inserire codice HTML per la creazione di link nei contenuti.

Durante il caricamento della pagina HTML, in particolare quando essa è già disponibile ma non ancora completamente visibile(metodo JQuery().ready(){...}), la classe LinksGenerator filtra i contenuti delle aree di maggior interesse ed esegue la creazione dei link se avviene il match tra il termine e una chiave dell'array associativo.

Ho scelto questa soluzione fondamentalmente per i seguenti motivi:

  • Mi permette di separare i contenuti da visualizzare dal modo scelto per visualizzarli;
  • Mi permette di linkare risorse automaticamente in tutto il sito;
  • Mi permette di rimuovere i link interrotti automaticamente in tutto il sito;
  • Mi permette di concentrarmi esclusivamente sui contenuti;
  • Il filtro Javascript sfrutta le risorse hardware del client utente;

Come Funziona il Filtro?

Dunque, il filtro Javascript che ho creato, per poter comprendere dove creare un link all'interno di una porzione di testo qualunque, esegue schematicamente le seguenti operazioni:

  • Riceve il testo da lavorare;
  • Scompone il testo in un array contenente tutte le parole presenti;
  • Per ogni parola esegue un accesso all'array associativo contenente tutti i link;
  • Se avviene il match chiave/termine, avviene la trasformazione della parola in link HTML all'URL estratta dall'array;

Per eseguire questo compito ho creato due classi Javascript e, per comodità, utilizzo il framework JQuery.

E' ora di iniziare ad analizzare parte del codice sorgente scritto: iniziamo dalla classe LinksGenerator.

La classe LinksGenerator

Questa classe è, tra le due create, quella di livello più alto: espone funzionalità complesse, le quali incapsulano le reali logiche di business che provvedono ognuna alla risoluzione di un specifico problema, fornisce proprietà e metodi di classe per governare le funzionalità dell'altro componente, ovvero la classe LinkDecorator, che vedremo in seguito.

Il codice sorgente che segue è ben commentato ed auto esplicativo.

/**
 * Elenco dei caratteri che vengono cercati in testa 
 * ed in coda ad ogni singola parola.
 * Utilizzato per aumentare il grado di precisione 
 * nel fornire la chiave di accesso alla mappa di URL.
 */
LinksGenerator.PERMITTED_CHARS 
    = ['.',',',':','\'',';','?','(',')','"','\n'];

/**
 * Controlla se il carattere passato come parametro 
 * e' da considerare, o scartare, nella costruzione 
 * del link.
 */
LinksGenerator.isAPermittedChar = function(c){
  for(var i=0;
      i<LinksGenerator.PERMITTED_CHARS.length;
      i++){

    if(c == LinksGenerator.PERMITTED_CHARS[i])
      return false;
  }
  return true; 
}

/**
 * Classe per decorare un testo senza link.
 * La classe LinksGenerator provvede a sostituire 
 * tutte le parole che sono anche chiavi della mappa 
 * LinksGenerator.LINKS con i relativi link abbinati.
 */
function LinksGenerator(t){
  //Proprieta' che conterra' il codice HTML 
  //del testo trasformato.
  var html = '';

  //Proprieta' che conserva il testo originale
  var text = t;

  //Array di tutte le parole presenti nel testo originale
  var words = text.split(' ');
        
  /*
  Con ogni parola presente in words[] 
  prova a generare un link utilizzando una nuova istanza 
  della classe LinkDecorator.
  */
  for(var i=0; i<words.length; i++){
    var l = new LinkDecorator(words[i]);
    html += l.getHtmlLink();
  }

  /*
  Getter pubblici per accedere al contenuto delle proprietà
  dall'esterno del componente.
  */
  this.getHtml = function(){return html;}
  this.getText = function(){return text;}
  this.getWords = function(){return words;}
}

/**
 * Mappa chiave/valore con l'elenco dei link 
 * da utilizzare in abbinamento
 * alla relativa chiave
 */
LinksGenerator.LINKS = new Array();
LinksGenerator.LINKS['java'] 
    = 'http://java.sun.com/reference/api/';
LinksGenerator.LINKS['sun'] 
    = 'http://www.sun.com';
LinksGenerator.LINKS['jee'] 
    = 'http://java.sun.com/javaee/';
LinksGenerator.LINKS['jme'] 
    = 'http://java.sun.com/javame/';
LinksGenerator.LINKS['struts'] 
    = 'http://struts.apache.org/';
LinksGenerator.LINKS['utf-8'] 
    = 'http://it.wikipedia.org/wiki/UTF-8';

Come si può notare, la classe LinksGenerator non esegue nessuna trasformazione, contiene in se solo la logica con cui eseguire il parsing della porzione di testo passatale e con cui ricomporre il testo con le eventuali
modifiche apportate.

L'operazione di trasformazione della singola parola con il link abbinato è un compito che ho preferito delegare ad un componente specializzato, o meglio un decoratore: la classe LinkDecorator.

In questa maniera, se avrò bisogno un giorno di riutilizzare il componente per eseguire qualche altra operazione, mi basterà parametrizzare il costruttore della classe LinksGenerator in modo da accettare anche il tipo del nuovo decoratore da utilizzare.

La classe LinkDecorator

Dunque, questo componente, o decoratore, è specializzato nell'eseguire tutte le operazioni necessarie per la creazione di un link HTML a partire da una parola.

Per migliorare l'efficacia della ricerca, il componente esegue dei test e delle operazioni sulla parola ricevuta con il fine di aumentare le possibilità di trovare la chiave corretta e corrispondente nell'array associativo LinksGenerator.LINKS.

Queste operazioni prevedono l'eliminazione dei caratteri, previsti e censiti nell'array LinksGenerator.PERMITTED_CHARS, in testa ed in
coda al termine analizzato.

Passiamo ora ad analizzare il codice sorgente della classe LinkDecorator.

/**
 * Classe per la creazione di un link.
 * La classe LinkDecorator provvede a sostituire 
 * la parole in ingresso con il relativo link.
 * La classe provvede anche a rimuovere caratteri 
 * di punteggiatura in testa ed in coda alla parola.
 */
function LinkDecorator(w){
  var word = $.trim(w);
  var len = word.length;
        
  /*
  Conserva il primo carattere della parola per 
  'limarla' nel caso non si verifichi un match immediato.
  */
  var firstChar = word.charAt(0);
        
  /*
  Conserva l'ultimo carattere della parola per 'limarla'
  nel caso non si verifichi un match immediato.
  */
  var lastChar = word.charAt(len-1);
        
  /*
  Verifica che la parola inizi con un carattere 
  da rimuovere.
  */
  var cleanOnStart 
      = LinksGenerator.isAPermittedChar(firstChar);
        
  /*
  Verifica che la parola finisca con un 
  carattere da rimuovere.
  */
  var cleanOnEnd 
      = LinksGenerator.isAPermittedChar(lastChar);
        
  var link = null;
 
  /*
  Se non sarà creato alcun link allora verrà 
  restituita la parola ricevuta in ingresso 
  con uno spazio bianco in coda.
  */
  var html = word+'&nbsp;';
 
  if(! cleanOnStart){
    word = word.substring(1);
  }
 
  if(! cleanOnEnd){
    word = word.substring(0,len-1);
  }
 
  /*
  Esegue l'accesso alla mappa per trovare 
  il link abbinato.
  */
  link = LinksGenerator.LINKS[word.toLowerCase()];
        
  /*
  Se esiste un link abbinato al termine,
  il componente creerà il codice HTML necessario
  */
  if(link != null){
    html = createLink();
  }
 
  /**
   * Crea il codice HTML necessario per il link
   * e ricompone la punteggiatura presente 
   * nel termine originale.
   */
  function createLink(){
    var t = '<a href="' + link 
        + '" target="_blank">' + word
        + '<img src="./img/external_link.gif"/></a>';
                        
    if(! cleanOnStart) t = firstChar+t;
    if(! cleanOnEnd) t = t+lastChar;
    return t+'&nbsp;';
  }
 
  /**
   * Metodi getter per accedere in sola lettura
   * alle proprietà del componente.
   */
  this.getHtmlLink = function(){return html;}
  this.getLink = function(){return link;}
}

Il componente è tanto semplice quanto funzionale.

E' da notare che il decoratore per verificare se deve eliminare caratteri di punteggiatura presenti in testa o in coda al termine, incapsula il metodo di classe LinksGenerator.isAPermittedChar().

Non rimane che mettere in funzione le classi javascript esposte: per fare questo ho creato una piccola pagina HTML di esempio.

Qui è riportato solo il codice sorgente saliente, cioè la creazione di tante istanze della classe LinksGenerator quanti sono i paragrafi presenti nel BODY della pagina HTML.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xml:lang="en" lang="en">
<head>
  <title>
    Pagina di esempio del sistema di Filtraggio 
    e sostituzione del testo scritto in JavaScript 
    e presentato da Agrati Mirko.
    Articolo presente sul sito dell'autore: 
    http://mirkoagrati.blogspot.com
  </title>
  <script 
    src="http://jqueryjs.googlecode.com/files/jquery-1.2.2.min.js"
    type="text/javascript"></script>
  <script type="text/javascript">
    
 //Dichiarazione delle classi
 ..........................
 
  //Utilizzo del framework JQuery per realizzare la decorazione
  $(document).ready(
    function(){            
      $('p').each(
        function(){
          $(this).html(
            new LinksGenerator($(this).html()).getHtml()
          );
        }      
      );
    }
  );
  
  </script>
</head>
<body>
  <p>Ciao a tutti, programmo da molti anni con le tecnologie 
  SUN e ormai ho accumulato un certo <i>bagaglio</i>.</p>
  <p>Programmo in Java, che utilizzo quotidianamente in ambiente 
  enterprise con la tecnologia JEE, ho creato applicativi 
  per cellulari con la tecnologia jme.</p>
  <p>Mi piace lavorare seguendo il design pattern MVC,
  e mi trovo molto bene in ambito web con il framework Struts.</p>
  <p>Ultimamente sto attrezzando i vari applicativi 
  all'utilizzo del charset UTF-8.</p>
</body>
</html>

Come si nota, nel codice sorgente non sono presenti link HTML.

Potete trovare il codice sorgente e una demo funzionante cliccando qui.

Alla prossima,
MA.

domenica 7 marzo 2010

XSD: le Tipologie di Base dei Dati

Per poter descrivere qualunque tipo di informazione, semplice o complessa che sia, dobbiamo necessariamente chiarirne la natura ed il valore. Per fare questo, XSD mette a disposizione una lunga lista di tipologie di base per poter agevolmente descrivere il tipo di dato rappresentato.

I tipi di dati di base in XSD si distinguono in:

  • predefiniti o built-in;
  • derivati (poichè derivano dai predefiniti);

Tipi Predefiniti

Tipi Built-in
Nome tipo di Dato Descrizione
stringStringhe di caratteri
booleantrue/false
decimalNumeri di tipo Decimal
float Numeri a virgola mobile e precisione singola a 32-bit
doubleNumeri a virgola mobile e precisione doppia a 64-bit
durationIntervallo di tempo
dateTimeData completa di ora
TimeOra minuti e secondi
DateData
gYearMonth Rappresenta un mese dell'anno secondo il calendario Gregoriano
gYearRappresenta un anno secondo il calendario Gregoriano
gMonthDay Rappresenta una data ricorrente secondo il calendario Gregoriano. Ad esempio il 6 Dicembre di ogni anno
gDay Rappresenta un giorno ricorrente secondo il calendario Gregoriano. Ad esempio il 6 di ogni mese
gMonth Rappresenta un mese ricorrente secondo il calendario Gregoriano. Ad esempio Dicembre di ogni anno
hexBinary Rappresenta un dato binario secondo la codifica esadecimale
base64Binary Rappresenta un dato binario secondo la codifica Base64
anyURI Rappresenta una URI come definita dallo standard RFC 2396
QName Rappresenta un nome qualificato: necessita di un prefisso associato ad un namespace
NOTATIONRappresenta un tipo di attributo NOTATION

Tipi Derivati

I tipi di dati derivati dai precedenti sono elencati di seguito.

Rappresenta un integer con un valore minimo di -128 ed uno massimo di 127
Tipi Derivati
Nome tipo di DatoDescrizione
normalizedString Rappresenta una stringa privata degli spazi bianchi in eccesso
TokenRappresenta una stringa tokenizzata
LanguageRappresenta un identificatore di linguaggio
IDREFS Rappresenta il tipo attributo IDREFS. Contiene un insieme di valori di tipo IDREF
ENTITIES Rappresenta il tipo attributo ENTITIES. Contiene un insieme di valori ENTITY
NMTOKEN Rappresenta il tipo attributo NMTOKEN cioe' l'insieme dei caratteri di un nome in qualsiasi combinazione.Non ha restrizioni nel carattere iniziale
NMTOKENS Rappresenta il tipo attributo NMTOKENS: contiene un insieme di valori NMTOKEN
Name Rappresenta i nomi in XML. Un Name e' un token che inizia con una lettera, un underscore o due punti
NCName Rappresenta i nomi che non possono iniziare con il carattere dei due punti.
ID Rappresenta l'attributo ID definito in XML 1.0. ID deve essere un NCName e deve essere unico in tutto il documento XML
IDREF Rappresenta un riferimento a un elemento che ha un attributo ID identico all'IDREF specificato. IDREF deve essere un NCName
ENTITYRappresenta il tipo attributo ENTITY in XML 1.0
integerRappresenta una sequenza di numeri decimali con un segno iniziale opzionale
nonPositiveIntegerRappresenta un integer minore o
uguale a zeronegativeIntegerRappresenta un integer minore di zero. Deve essere preceduto dal segno -
long Rappresenta un integer con un valore minimo di -9223372036854775808 ed un massimo di 9223372036854775807
intRappresenta un integer con un valore minimo di -2147483648 ed uno massimo di 2147483647
short Rappresenta un integer con un valore minimo di -32768 ed uno massimo di 32767
byte
nonNegativeIntegerRappresenta un integer maggiore o uguale a zero
unsignedLongRappresenta un integer con un valore minimo di zero ed uno massimo di 18446744073709551615
unsignedIntRappresenta un integer con un valore minimo di zero ed uno massimo di 4294967295
unsignedShortRappresenta un integer con un valore minimo di zero ed uno massimo di 65535
unsignedByte Rappresenta un integer con un valore minimo di zero ed uno massimo di 255
positiveIntegerRappresenta un integer maggiore di zero

Di fronte a questo vasto assortimento di tipologie di dati utilizzabili, vien proprio difficile non riuscire a descrivere accuratamente un'informazione.

Alla prossima,
MA.

sabato 6 marzo 2010

SEO: Le conseguenze di alcune parole

In questo mio articolo vorrei soffermarmi a discutere sul peso che alcune parole se usate hanno e potrebbero avere su eventuali abbassamenti del Ranking di qualunque sito.

Sebbene non esista un elenco stabilito ne tanto meno un tool speciale per individuare precisamente quelle parole, si possono facilmente intuire quali categorie di termini non dovrebbero essere utilizzate:

  • Volgarità;
  • Parole Politicamente Scorrette;
  • Poison words o parole velenose;
  • Poison keywords;
  • Stop words;

Nel momento in cui scegliamo le parole chiave che andranno a caratterizzare il nostro sito, e che formeranno la base per la sua ottimizzazione, bisogna sapere e ricordare che alcuni termini non solo non porteranno traffico al sito, ma potrebbero anche causare un abbassamento di Ranking.

Si deduce che tali parole e anche key phrases sono considerate dai motori di ricerca dei termini proibiti o velenosi.

I search engines durante le operazioni di ricerca ed indicizzazione fanno particolare attenzione a verificare la presenza di tali termini in posti specifici delle pagine HTML, come per esempio:

  • nell'elemento TITLE child(in italiano figlio) di HEAD;
  • negli elementi specifici per la formattazione dei titoli (H1, H2, H3 ecc...);
  • All'interno degli elementi A utilizzati per la creazione di link a determinate URLs

La presenza di tali termini nei suddetti luoghi del codice HTML può influenzare negativamente il posizionamento delle pagine nei risultati che i motori di ricerca restituiscono alle queries degli utenti.

Nel caso delle volgarità il condizionale è un obbligo: se per esempio si esegue la ricerca di una parolaccia qualunque motore di ricerca restituirà montagne di risultati. E' quindi vero che, pur in qualche maniera (visto che nessuno conosce esattamente che tipologie di filtri hanno adottato i motori di ricerca) il processo di indicizzazione di quei contenuti avviene; è altrettanto vero però che il ranking di quei siti sarà, se non lo è già stato, fortemente penalizzato!

Per ciò che concerne l'utilizzo di termini politically incorrect è da considerare l'eventualità che in paesi dove vi sono ancora forme di censura on line, come Cina e Iran, si potrebbe essere completamente esclusi dal processo di indicizzazione e quindi non apparire mai tra i risultati delle ricerche.

Le poison words, dette anche parole velenose o proibite, sono termini ai quali i motori di ricerca sono, diciamo, molto sensibili e che possono generare in essi sospetti di veridicità ed attendibilità verso il sito che ospita tali contenuti. Potrebbe essere sospetto un sito di ricette di cucina ma che nel quale si trovano anche informazioni riguardanti le partite di poker.

Poison keywords è usato per riferirsi all'utilizzo eccessivo delle parole chiave all'interno di un sito. Questo tecnica volta ad aumentare la densità dei termini strategici è comunemente conosciuto come stuffing e, manco a dirlo, può determinare l'abbassamento del Rank e sospetti riguardo un'eventuale attività di spam.

Le Stop words sono parole così comuni che la loro presenza e densità non vengono considerate né dai motori di ricerca né all'interno dei circuiti Pay per Click.

Buona SEO a tutti.

Alla prossima,
MA