Este cap�tulo demuestra c�mo usar JAXB para unir un DTD a un conjunto de clases Java. Para generar la clases, realizamos estos pasos:
- Escribir un esquema de uni�n, que contiene las instrucciones de c�mo unir un DTD a las clases.
- Ejecutar el compilador de esquema con el DTD y el esquema de uni�n como entradas para generar el c�digo fuente.
- Compilar el c�digo fuente para generar las clases.
Los dos cap�tulos siguientes muestran c�mo utilizar las clases para construir representaciones de datos desde documentos XML y trabajar con los datos. El ejemplo que estos cap�tulos usan es la sencilla aplicaci�n de libro de cheques que se describi� en el escenario 1: Balancear un Libro de Cheques. Con esta aplicaci�n, podremos registrar transacciones en un libro de cheques y determinar el balance de la cuenta controlada.
El ejemplo checkbook usa el checkook.dtd, que est� localizado en el directorio examples/checkbook de nuestra instalaci�n de JAXP. La primera secci�n de este cap�tulo explica el DTD checkbook.
�El DTD de Ejemplo: "checkook.dtd"
Antes de crear la aplicaci�n checkbook, deber�amos entender el DTD en el que est� basada. Esta secci�n describe brevemente el checkook.dtd mostrado aqu�:
<!ELEMENT checkbook ( transactions, balance ) > <!ELEMENT transactions ( deposit | check | withdrawal )* > <!ELEMENT deposit ( date, name, amount )> <!ATTLIST deposit category ( salary | interest-income | other ) #IMPLIED > <!ELEMENT check ( date, name, amount, ( pending | void | cleared ), memo? ) > <!ATTLIST check number CDATA #REQUIRED category ( rent | groceries | other ) #IMPLIED > <!ELEMENT withdrawal ( date, amount ) > <!ELEMENT balance (#PCDATA) > <!ELEMENT date (#PCDATA ) > <!ELEMENT name (#PCDATA) > <!ELEMENT amount (#PCDATA) > <!ELEMENT memo (#PCDATA) > <!ELEMENT pending EMPTY > <!ELEMENT void EMPTY > <!ELEMENT cleared EMPTY >
Un documento XML debe tener exactamente un elemento de ra�z, pero el documento puede seleccionar que elemento utilizar como su elemento ra�z del DTD. Para nuestro ejemplo del checkbook, tanto checkbook como transactions se pueden utilizar como elementos ra�z por documentos XML de este ejemplar de DTD. La secci�n Crear el Esquema de Uni�n M�nimo Requerido muestra c�mo especificar en el esquema de uni�n qu� elementos de un documento XML v�lido pueden utilizarse como elementos ra�z. Seg�n el DTD, un checkbook contiene transacciones que se hacen contra la cuenta y un balance que representa la cantidad de dinero que la cuenta contiene. El conjunto de transacciones, seg�n la definici�n del elemento de las transacciones, contiene cero o m�s dep�sitos, cheques, o reintegros.
Una transaci�n deposit consta de:
- La fecha en que se hizo.
- El nombre de la persona o empresa que proporcion� el dinero para el dep�sito.
- La cantidad depositada.
El elemento deposit tambi�n tiene un atributo, llamado category, que describe la raz�n del dep�sito.
Una transaci�n check consta de :
- La fecha en la que se firmo el cheque.
- El nombre de la persona o empresa que recibe el cheque.
- La cantidad del cheque.
- El estado del cheque: cleared, void, o pending (todav�a no se ha cobrado).
- Un memo opcional.
El check tambi�n tiene dos atributos: number y category. El atributo number representa el n�mero de cheque. El atributo category representa el motivo por el que se ha extendido el cheque.
La transaci�n withdrawal (reintegro) consta s�lo de la fecha en la que se hizo y la cantidad reintegrada.
Los elementos balance , date , name , amount , y memo se han definido para tener contenido de car�cter. Este cap�tulo muestra c�mo generar varios tipos para el contenido de los elementos balance, data y amount.
Los elementos pending , void , y cleared no tienen contenido, por lo que se han definido como EMPTY.
�Escribir el Esquema de Uni�n
No necesitamos proporcionar una declaraci�n de uni�n para cada componente del DTD. El compilador de esquema asume declaraciones de uniones por defecto si no se proporciona una uni�n particular. Sin embargo, si no est�mos satisfechos con las uniones por defecto, necesitamos proporcionar declaraciones de uni�n en el esquema de uni�n para generar las clases que deseamos.
Esta secci�n nos muestra:
- C�mo escribir el esquema de uni�n m�nimo, que es un esquema de uni�n que contiene las declaraciones m�nimas que permitir�n al compilador de esquema generar las clases.
- Qu� c�digo generar� el compilador de esquema bas�ndose en el esquema de uni�n m�nimo y el DTD.
- C�mo a�adir declaraciones al esquema de uni�n m�nimo para que el compilador de esquema genere el c�digo que queremos, no s�lo el c�digo por defecto.
Esta gu�a explica detalladamente c�mo escribir el esquema de uni�n. El esquema de uni�n es lo que utilizamos para controlar qu� tipo de c�digo genera el compilador de esquema. Por lo tanto, es importante que entendamos c�mo el compilador de esquema interpreta las declaraciones que proporcionamos en el esquema de uni�n y lo que el compilador de esquema asume si no proporcionamos las declaraciones de uni�n para una declaraci�n del DTD. Adem�s, el lenguaje de uni�n y el compilador de esquema que interpreta el esquema de uni�n son los suficientemente proderosos como para hacer asunciones razonables, pero todav�a nos permiten mucha flexibilidad para definir c�mo el DTD est� unido a las clases.
Aunque el compilador del esquema puede producir una uni�n razonable en el caso simple, cuando veamos qu� clase de c�digo genera el compilador de esquema basandose en el DTD checkbook y un esquema de uni�n m�nimo, entenderemos la importancia de proporcionar declaraciones de uni�n de modo que podamos generar el c�digo apropiado para nuestra aplicaci�n.
�Crear el Esquema de Uni�n M�nimo Requerido
Tanto si deseamos o no validar las declaraciones de uni�n por defecto asumidas por el compilador de esquema, necesitamos proporcionar un esquema de uni�n. Para crear el esquema de uni�n para el DTD del checkbook:
- Creamos un nuevo fichero de texto llamado checkook.xjs.
- En checkook.xjs, escribimos:
<xml-java-binding-schema version="1.0ea">
Esta etiqueta identifica el fichero como un esquema de uni�n.
- Todos los esquemas de uni�n deben declarar al menos un elemento ra�z. En nuestro ejemplo, queremos declarar dos elementos
ra�z: checkbook y transactions. Para declarar los elementos raiz,
escribimos:
<element name=checkbook type=class root=true /> <element name=transactions type=class root=true />
Para declarar estos elementos ra�z, utilizamos la declaraci�n de uni�n del elemento para unir un tipo de elemento a una clase. El valor del atributo debe ser el nombre del elemento tal como aparece en el DTD. El valor del tipo de atributo es class en este caso porque �stos son elementos ra�z y se deben limitar a las clases. El atributo root debe ser igual a true porque queremos que los ejemplares del documento XML del DTD checkbook pudieran declarar checkbook o transactions como elementos ra�z.
- Introducimos la etiqueta final para el elemento xml-java-binding-schema:
</xml-java-binding-schema>
Ahora tenemos un esquema de uni�n legal desde el cual el compilador de esquema puede generar clases. De acuerdo con el DTD, el compilador de esquema hace asunciones con respecto a c�mo unir las otras declaraciones del DTD para los cuales no proporcionamos declaraciones de uni�n. La siguiente secci�n explica las declaraciones de uni�n asumidas por el compilador de esquema bas�ndose en el DTD checkbook y el esquema de uni�n actual. Una vez que entendamos qu� tipo de c�digo se produce desde un esquema de uni�n m�nimo, la escritura el resto del esquema de uni�n es f�cil.
�Entender las Declaraciones por Defecto
Las declaraciones de uni�n que el compilador de esquema asume bas�ndose en el DTD checkbook y el esquema de uni�n m�nimo son:
<element name="checkbook" type="class" root=true> <content> <element-ref name="transactions"/> <element-ref name="balance"/> </content> </element> <element name="transactions" type="class" root=true> <content property="content"/> </element> <element name="deposit" type="class"> <attribute name="category"/> <content> <element-ref name="date"/> <element-ref name="name"/> <element-ref name="amount"/> </content> </element> <element name="check" type="class"> <attribute name="number"/> <attribute name="category"/> <content property="content"/> </element> <element name="withdrawal" type="class"> <content> <element-ref name="date"/> <element-ref name="amount"/> </content> </element> <element name="balance" type="value"/> <element name="date" type="value"/> <element name="name" type="value"/> <element name="amount" type="value"/> <element name="memo" type="value"/> <element name="pending" type="class"/> <element name="void" type="class"/> <element name="cleared" type="class"/>
El resto de esta secci�n explica cada una de estas declaraciones de union.
�Declaraci�n de Uni�n de Elementos
El compilador de esquema asume diversas declaraciones de uni�n para los elementos dependiendo de qu� tipo de contenido tienen o si tienen atributos. Para los elementos simples, que tienen solamente el contenido del tipo car�cter y ningun atributo, el compilador de esquema asume que los elementos est�n unidos a las propiedades dentro de la clase de su elemento padre. Los elementos balance, date, name y amount son elementos simples y por eso el compilador de esquema asume estas uniones para ellos:
<element name="balance" type="value"/> <element name="date" type="value"/> <element name="name" type="value"/> <element name="amount" type="value"/> <element name="memo" type="value"/>
El nombre de los atributos debe ser el nombre del elemento tal como aparece en el DTD. El tipo del atributo es value en estos casos porque estos elementos est�n unidos a propiedades, no a clases. �stas declaraciones de uni�n har�n que compilador de esquema genere estas propiedades:
String getBalance(); void setBalance(String x); String getDate(); void setDate(String x); String getName(); void setName(String x); String getAmount(); void setAmount(String x); String getMemo(); void setMemo(String x);
Una propiedad String est� muy bien para los elementos name y memo. Sin embargo, para balance y amount, necesitamos un tipo que represente mejor valores de moneda. De forma similar date se debe limitar a una propiedad que acepte y devuelva una cierta clase de tipo que represente mejor una fecha. La secci�n Especificar Tipos mostrar� c�mo generar las propiedades que aceptan y devuelven diferentes tipos.
Para el resto de los tipos de elementos, el compilador de esquema asume que los elementos est�n unidos a clases. Si un elemento contiene cualquier cosa distinta de contenido de caracteres o tiene atributos, el compilador de esquema lo unir� a una clase. Los elementos deposit, check, y withdrawal tienen elementos de contenido, y deposit y check tiene atributos. Estos elementos tambi�n est�n unidos a clases. Las uniones por defecto para estos elementos son:
<element name="deposit" type="class" > <element name="check" type="class" > <element name="withdrawal" type="class" >
Observa que no necesitamos especificar el atributo root como hicimos en la secci�n Crear el Esquema de Uni�n M�nimo Requerido. El compilador de esquema asume que el valor de este atributo es false. S�lo necesitamos especificar el valor de este atributo como true si queremos el ejemplar del documento XML tenga la posibilidad de usar ese elemento como elemento ra�z. De la declaraci�n de uni�n del elemento deposit el compilador de esquema genera esta definici�n de clase y este constructor:
public class Deposit extends MarshallableObject { public void Deposit();
Las clases Check , y Withdrawal se parecer�n mucho a �sta.
Los elementos pending , void , y cleared tienen contenido EMPTY y est�n includos en un modelo de grupo de elecci�n, y por eso est�n unidos a clases, como se especifica en sus declaraciones de tipos de atributos:
<element name="pending" type="class"/> <element name="void" type="class"/> <element name="cleared" type="class"/>
�Declaraci�n de Uni�n de Atributos
El DTD checkbook define tres atributos. El elemento deposit tiene un atributo category, y el elemento check tiene un atributo number y tambi�n un atributo category:
<!ELEMENT deposit ... <!ATTLIST deposit category ( salary | interest-income | other ) #IMPLIED > <!ELEMENT check ... <!ATTLIST check number CDATA #REQUIRED category ( rent | groceries | other ) #IMPLIED >
Todos estos atributos toman valores at�micos, en vez de valores compuestos, y por eso el compilador de esquema asume que estos atributos est�n unidos a propiedades String, como se especifica en esta declaraciones de uni�n por defecto:
<element name=deposit type=class > <attribute name=category /> ... <element name="check" type="class"> <attribute name=number /> <attribute name=category /> ...
Dentro de la clase Deposit, el compilador de esquema genera esta propiedad para representar el atributo category:
void setCategory(String x); String getCategory();
Dentro de la clase Check, el compilador de esquema genera estas propiedades para representar a los atributos number y category:
void setNumber(String x); String getNumber(); void setCategory(String x); String getCategory();
Observa que la propiedad number acepta y devuelve un String. La secci�n Especificar Tipos mostrar� c�mo modificar el esquema para requisitos de uniones particulares de modo que el compilador de esquema genere una propiedad number que acepte y devuelva un int. Asimismo, la secci�n Crear Tipos Enumerados mostrar� c�mo generar un tipo enumerado para la propiedad category.
�Declaraci�n de Uni�n de Contenido
El declaraci�n de uni�n de contenido es lo m�s complicado, reflejando el n�mero infinito de maneras en que podemos especificar el contenido XML, pero JAXB lo hace f�cil para nosotros. El tipo m�s com�n de modelo de contenido es una secuencia simple, no-repetitiva, por ejemplo (a, b, c, d). Si utilizamos este tipo de modelo de contenido, muy probablemente no necesitaremos especificar un declaraci�n de uni�n para �l porque el compilador de esquema genera una propiedad separada para cada elemento de la secuencia, que es generalmente lo que deseamos.
La mayor�a de los elementos que hay en checkook.dtd tienen contenido secuencial simple y no repetitivo. Para estos elementos, el compilador de esquema asume las declaraciones de uni�n mostradas en negrita:
<element name="checkbook" type="class" root=true> <content> <element-ref name="transactions"/> <element-ref name="balance"/> </content> </element> <element name="deposit" type="class"> <attribute name="category"/> <content> <element-ref name="date"/> <element-ref name="name"/> <element-ref name="amount"/> </content> </element> <element name="withdrawal" type="class"> <content> <element-ref name="date"/> <element-ref name="amount"/> </content> </element>
El declaraci�n de uni�n element-ref se utiliza para unir un ejemplar de un elemento en un modelo de contenido a una propiedad en la clase del elemento padre. La declaraci�n del elemento que corresponde al ejemplar del elemento une el elemento a s� mismo, incluyendo la uni�n del elemento a su tipo. Por defecto, el compilador de esquema genera propiedades String desde todas las declaraciones de uniones element-ref que se refieran a elementos simples. La propiedad por defecto para el elemento name es:
public class Deposit { ... String getName(); void setName(String x);
El compilador de esquema tambi�n genera una propiedad String para balance, date, y amount. La clase Checkbook contiene una propiedad para el elemento balance. Las clases Deposit y Withdrawal tienen propiedades para los elementos date y amount. En la secci�n Especificar Tipos veremos que los tipos de estas propiedades cambian cuando utilizamos el atributo convert en las declaraciones de uni�n correspondientes al elemento.
Como especificamos que el elemento transactions est� unido a una clase, la declaraci�n de uni�n element-ref para el elemento transactions har� que el compilador de esquema genere esta propiedad en el clase Checkbook:
Transactions getTransactions(); void setTransactions(Transactions x);
Si un elemento contiene algo distinto a una secuencia simple no repetitiva, el compilador de esquema asumir� la declaraci�n de uni�n de la propiedad general-content, que es:
<content property=content />
Los elementos transactions y check no tienen secuencias simples y no repetitivas como contenidos, por eso el compilador de esquema asume estas declaraciones de uni�n para ellos:
<element name="transactions" type="class" root=true> <content property="content"/> </element> ... <element name="check" type="class"> <attribute name="number"/> <attribute name="category"/> <content property="content"/> </element>
Se usa la declaraci�n general-content para unir un modelode grupo completo, incluyendo modelos de grupos anidados, a una propiedad. Este declaraci�n no es muy �til para nuestros prop�sitos porque deseamos tener acceso a los elementos individuales en estos modelos de contenido. Esta declaraci�n es �til para definir uniones m�s flexibles si anticipamos que nuestro DTD cambiar� en el futuro. Para m�s informaci�n sobre esta declaraci�n, puedes ver la secci�n Manejar la Evoluci�n del Esquema.
Desde estas declaraciones de uni�n, el compilador de esquema genera esta propiedad en las clases Transactions y Check:
List getContent(); void emptyContent(); void deleteContent();
El m�todo getContent devuelve una lista modificable que contiene el valor actual de las propiedades. El m�todo emptyContent descarta los valores de la lista y crea una nueva lista, vac�a. El m�todo deleteContent borra la lista.
�Personalizar el Esquema de Uni�n
Ahora que entendemos las declaraciones de uni�n que el compilador del esquema asumir� bas�ndose en el DTD y el esquema de uni�n m�nimo, es f�cil escribir el esquema de uni�n: Todo lo que necesitamos escribir son las declaraciones de uniones que no son asumidas por el compilador de esquema. Esta secci�n explica cada una de las personalizaciones para requisitos particulares que podemos hacer al esquema de uni�n para conseguir las clases que deseamos.
El esquema de uni�n que usaremos para la aplicaci�n checkbook es:
<xml-java-binding-schema version="1.0ea"> <element name="checkbook" type="class" root="true" /> <element name="transactions" type="class" root="true"> <content> <choice property="entries" collection="list" supertype="Entry" /> </content> </element> <element name="balance" type="value" convert="BigDecimal"/> <element name="amount" type="value" convert="BigDecimal" /> <element name="date" type="value" convert="TransDate" /> <element name="deposit" type="class" > <attribute name="category" convert="DepCategory" /> </element> <element name="check" type="class" > <content> <element-ref name="date"/> <element-ref name="name" /> <element-ref name="amount" /> <choice property="pend-void-clrd"/> </content> <attribute name="number" convert="int" /> <attribute name="category" convert="CheckCategory" /> </element> <conversion name="BigDecimal" type="java.math.BigDecimal" /> <conversion name="TransDate" type="java.util.Date" parse="TransDate.parseDate" print="TransDate.printDate" /> <enumeration name="DepCategory" members="salary interest-income other"/> <enumeration name="CheckCategory" members="rent groceries other"/> <interface name="Entry" members="Deposit Check Withdrawal" properties="date amount" /> </xml-java-binding-schema>
Esta secci�n nos lleva a trav�s de este esquema de uni�n como si lo hubieramos escrito desde �l principio. Ya hemos incorporado las uniones del elemento de ra�z en la secci�n Crear el Esquema de Uni�n M�nimo Requerido, por eso empecemos con ella:
- Reemplazamos la etiqueta de elemento vac�o (/>) con un angulo a la derecha (>) y a�adimos una etiqueta final para
la declaraci�n de elemento ra�z transactions porque le vamos a a�adir declaraciones de
contenido:
<xml-java-binding-schema version="1.0ea"> <element name="checkbook" type="class" root="true" /> <element name="transactions" type="class" root="true" > </element>
- Antes de que empezar a escribir la declaraci�n de uni�n personalizada, las primeras declaraciones que necesitamos en nuestro
esquema de uni�n son las declaraciones de uni�n de elemento para los elementos deposit y
check porque especificaremos tipos para sus atributos y personalizaremos el modelo de contenido
del elemento check . El compilador de esquema necesita estas declaraciones para poder generar
las propiedads para los atributos y el contenido de elecci�n en la clase correcta y para los elementos correctos.
Detntro del elemeno ra�z de nuestro esquema de uni�n, despu�s de las declariones de uni�n de elementos ra�ces, introducimos:
<element name=deposit type=class > </element> ... <element name=check type=class > </element>
M�s tarde a�adiremos el atributo personalizado y las declaraciones de uni�n de contenidos dentro de estos elementos.
�Especificar Tipos
Por defecto, el compilador de esquema genera m�todos get que devuelven un String y m�todos set que aceptan un String para todos los elementos y atributos simples. Por ejemplo, consideremos estas declaraciones de uni�n por defecto:
<element-ref name=amount /> ... <element name=amo unt type=value />
Desde estas declaraciones de uni�n, el compilador de esquema genera estos dos m�todos:
public String getAmount(); public void setAmount(String amount);
Si deseamos realizar algunos c�lculos con amount, necesitamos convertirla de un String a un alg�n otro tipo que permita que lo utilicemos en un c�lculos. Para los c�lculos que implican valores de moneda, el tipo BigDecimal es una buena opci�n porque representa n�meros decimales con signo de precisi�n arbitrar�a, y la clase BigDecimal proporciona los m�todos para aritm�tica b�sica.
Para especificar un tipo, utilizamos el declaraci�n de conversi�n para definir la conversi�n y el atributo convert de la declaraci�n de elemento o de atributo, dependiendo de si est�mos convirtiendo el tipo de una propiedad de elemento o de una propiedad de atributo, para referenciar la declaraci�n de conversi�n.
Especificar Tipos No Primitivos
Para definir una conversi�n de String a BigDecimal:
- A�adimos esta declaraci�n de conversi�n en cualquier lugar en el nivel superior (entre de las etiquetas
<xml-java-binding-schema version="1.0ea">) de nuestro esquema de uni�n, quiz�s despu�s
de la declaraci�n de uni�n del elemento check:
<conversion name=BigDecimal type=java.math.BigDecimal />
Cualquier declaraci�n de uni�n de elemento o de atributo que utilice esta conversi�n se refiere a ella por el nombre BigDecimal, seg�n lo especificado por el nombre del atributo. El valor del tipo de atributo es el tipo real al cual se convierte una propiedad.
- Para instruir al compilador de esquema para que genere una propiedad amount con un
tipo BigDecimal:
- A�adimos una declaraci�n de uni�n de elemento para el elemento amount en la parte
superior de nuestro esquema de uni�n:
<element name=amount type=value />
- A�adimos un atributo convert a la uni�n del elemento amount
y seleccionamos su valor a BigDecimal:
<element name=amount type=value convert=BigDecimal />
Estas declaraciones de uni�n producen est�s firmas de m�todos:
public java.math.BigDecimal getAmount(); public void setAmount(java.math.BigDecimal amount);
Declarar por separado la conversi�n de una uni�n de elemento nos permite reutilizar una conversi�n con otras uniones de elemento. Podemos hacer esto con el elemento balance, que tambi�n necesita estar unido a una propiedad BigDecimal.
Para instruir al compilador de esquema para que genere una propiedad balance con un tipo BigDecimal:
- >A�adimos una declaraci�n de uni�n de elemento para el elemento amount en la parte
superior de nuestro esquema de uni�n, y asignamos BigDecimal a su atributo
convert:
<element name=balance type=value convert=BigDecimal />
Tambi�n podemos convertir el tipo de la propiedad del elemento date a un java.util.Date. Puesto que una fecha se puede escribir de muchas formas distintas, necesitamos especificar c�mo se debe analizar la fecha cuando se desempaquete y c�mo debe imprimirse cuando se empaquete. La declaraci�n de conversi�n incluye los atributos parse y print para este prop�sito.
Para convertir los elementos date a un java.util.Date:
- A�adimos esta declaraci�n de conversi�n al esquema de uni�n:
<conversion name=TransDate type=java.util.Date parse=TransDate.parseDate print=TransDate.printDate/>
El nombre TransDate se refiere a una clase Java que necesitamos proporcionar. Esta clase contiene un m�todo est�tico parseDate que especifica c�mo analizar la fecha y un m�todo est�tico printDate que especifica c�mo imprimir la fecha.
- A�adimos una declaraci�n de uni�n de elemento para el elemento amount en la parte
superior de nuestro esquema de uni�n:
-
Para instruir al compilador de esquema para que genere una propiedad date
con la clase TransDate, a�adimos un atributo convert
a la declaraci�n de uni�n del elemento date y seleccionamos su valor a
TransDate:
<element name=date type=value convert=TransDate />
Estas declaraciones de uni�n producen estas firmas de m�todos:
public java.util.Date getDate(); public void setDate(java.util.Date x);
En el ejemplo de conversi�n BigDecimal, no necesitamos especificar un m�todo de an�lisis o de impresi�n porque la clase java.math.BigDecimal especifica un constructor que acepta un String y devuelve un BigDecimal y un m�todo toString que acepta un BigDecimal y devuelve un String. Para aplicar la conversi�n, el compilador de esquema genera c�digo para invocar al constructor y al m�todo toString.
Especificar Tipos Primitivos
Cuando especificaamos tipos primitivos, tales como int, no necesitamos proporcionar una declaraci�n de uni�n de conversi�n separada; simplemente a�adimos el atributo convert a la declaraci�n de uni�n del elemento o del atributo y asignamos el valor al tipo primitivo.
Dentro de la declaraci�n de uni�n del elemento check, a�adimos una declaraci�n de uni�n de atributo para el atributo number y especificamos un tipo int para su propiedad:
<element name=check type=class > <attribute name=number convert=int /> </element>
�Crear Tipos Enumerados
El atributo convert tambi�n se puede utilizar para especificar un tipo enumerado. En el lenguaje de programaci�n Java, representamos tipos enumerados con un tipo seguro enum, que es una clase cuyos ejemplares representan un conjunto fijo de valores.
Un atributo cuyo valor s�lo se pueda fijar a uno de un conjunto fijo de valores es un buen candidato para un tipo enumerado. El DTD checkbook tiene dos de estos atributos: el atributo category del elemento deposit y el atributo category del elemento check:
<!ATTLIST deposit ... category ( salary | interest-income | other ) #IMPLIED > <!ATTLIST check ... category ( rent | groceries | other ) #IMPLIED >
Para generar los tipos para estos atributos:
- Introducimos dos etiqueutas enumeration para cada conversi�n en el nivel superior del
esquema de uni�n, quiz�s detr�s de la declaraci�n de conversi�n de TransDate:
<conversion name=TransDate ... <enumeration <enumeration
- Para el atributo name de las etiquetas enumeration,
necesitamos un nombre �nico para cada enumeraci�n porque �mbas enumeraciones est�n al mismo nivel en el esquema de uni�n.
Introducimos DepCategory para la categoria de deposit
y CheckCategory para la categor�a de check.
<enumeration name="DepCategory" <enumeration name=CheckCategory
Estos nombres ser�n los nombres de las clases para representar los tipos seguros enums.
- A�adimos un atributo members a cada declaraci�n de enumeraci�n, y les asignamos
los posibles valores de cada atributo:
<enumeration name="DepCategory" members=salary interest-income other /> <enumeration name=CheckCategory members=rent groceries other />
- A�adimos las declaraciones de uni�n de attribute dentro de las declaraciones de
uni�n de los elementos deposit y check y asignamos el nombre
de la declaraci�n de enumeraci�n apropiada a cada atrributo convert de la declaraci�n
de uni�n de attribute:
<element name=deposit ... <attribute name=category convert=DepCategory /> ... <element name=check ... <attribute name=category convert=CheckCategory />
El compilador de esquema generar� esta clase desde la uni�n del atributo category de check:
public final class CheckCategory { public final static CheckCategory RENT; public final static CheckCategory GROCERIES; public final static CheckCategory OTHER; public static CheckCategory parse(String x); public String toString(); }
Veremos como trabajar con esta clase en el cap�tulo Construir Representaciones de Datos.
�Personalizar el Modelo de Declaraci�n de Uni�n de Contenido
Los modelos de contenido pueden ser muy complejos, y por eso el lenguaje de uni�n define muchas declaraciones diferentes de uniones para manejar diversos tipos de modelos de contenido. Definimos estas uniones con la declaraci�n de uni�n de contenido.
Podemos utilizar la declaraci�n de uni�n de contenido para definir dos tipos de declaraciones de modelo de contenido: la propiedad general-content y la propiedad de cotenido basado en modelos. Una propiedad general-content se utiliza para unir un modelo de contenido completoo a una propiedad. Esta declaraci�n no se utiliza para unir cualquier cosa en el DTD checkbook, pero es �til para definir uniones m�s flexibles si nos anticipamos a que el DTD cambiar� en el futuro. Para m�s informaci�n sobre esta declaraci�n, puedes ir a la secci�n Manejar la Evoluci�n del Esquema.
La declaraci�n de propiedad de contenido basado en modelo puede contener cuatro tipos de declaraciones para especificar diferentes tipos de uniones de grupos de modelo:
- element-ref, que especifica la uni�n de un ejemplar element dentro de otros elementos content. El constructor del elemento especifica la uni�n del propio elemento.
- choice, que especifica la uni�n de un grupo de modelo de elecci�n anidado a una propiedad.
- sequence, que especifica la uni�n de un grupo de modelo de elecci�n anidado a una propiedad.
- rest, que es una declaraci�n de uni�n m�s flexible que podemos utilizar para especificar cualquier tipo de contenido. Puedes econtrar m�s informaci�n en la secci�n Manejar la Evoluci�n del Esquema.
Recordamos de la secci�n Entender las Declaraciones de Uni�n por Defecto que el compilador de esquema asume una declaraci�n de uni�n de la propiedad general-content para los contenidos de los elemento check y transactions porque estos elementos no tienen modelos de contenido simples, con secuencias no repetitivas. En su lugar, estos elementos tienen grupos de modelos de elecci�n en sus declaraciones de contenido:
<!ELEMENT transactions ( deposit | check | withdrawal )* > ... <!ELEMENT check ( date, name, amount, ( pending | void | cleared ), memo? ) > ...
Esta secci�n muestra c�mo utilizar la declaraci�n de uni�n de elecci�n para hacer que el compilador de esquema genere propiedads m�s �tiles para el contenido de estos elementos. En el caso del contenido del elemento transactions, deseamos asignar el grupo entero a una propiedad de elecci�n. Para especificar la uni�n del contenido del elemento transactions:
- Dentro de la declaraci�n de uni�n del elemento transactions, introducimos
una etiqueta content y una declaraci�n de uni�n choice,
y asignamos el valor entries al atributo property:
<element name=transactions type=class root=true > <content> <choice property=entries
El compilador de esquema generar� una propiedad llamada Entries. Por ejemplo, el m�todo get se llamar� getEntries. La raz�n por la que utilizamos el nombre entries, es porque el nombre se refiere a un declaraci�n de interface, que la secci�n Crear Interfaces nos ense�ar� como crear.
- Como el modelo de contenido tiene un indicador de ocurrencia *, necesitamos
unir este contenidos a una propiedad collection. Introducimos el atributo
collection y le damos el valor list:
<choice property=entries collection=list />
Una propiedad collection puede representar un array o una List. En este caso, deber�amos unir el contenido a una List, porque �sta, al contrario que el array, nos permite a�adir m�s entradas durante la ejecuci�n.
- Introducimos la etiqueta final para la declaraci�n de uni�n content:
</content>
Estas declaraciones de uni�n producir�n est� propiedad en la clase Transactions:
public List getEntries(); public void deleteEntries(); public void emptyEntries();
El m�todo getEntry devuelve la lista de entradas completa. Una vez que consigamos la lista, podemos iterar a trav�s de la lista como lo har�amos con cualquier lista para llegar a una entrada determinada. La lista devuelta por getEntries es modificable: si modificamos una entrada en esta lista, cambiar� la entrada en el �rbol de contenido. El m�todo emptyEntries desecha los valores de la lista y crea una nueva lista, vac�a. El m�todo deleteEntries borra la lista.
Para especificar la uni�n del grupo del modelo de alecci�n anidado en el modelo de elecci�n del elemento check, dentro de la declaraci�n de uni�n del elemento check:
- Introducimos las declaraciones de uni�n element-ref para los elementos
date, name, y amount, e
insertamos la declaraci�n de uni�n de elecci�n como se muestra en negrita en la declaraci�n de uni�n del elemento
check:
<element name=check type=class > <attribute name=number convert=int /> <attribute name=category convert=CheckCategory /> <content> <element-ref name=date /> <element-ref name=name /> <element-ref name=amount /> <choice property=pend-void-clrd /> </content> </element>
El valor del atributo property es el nombre de la propiedad generada. Por ejemplo, el m�todo get se llamar� getPendVoidClrd. Si el contenido tambi�n contiene una opci�n, una secuencia, o un declaraci�n de uni�n de resto de contenido, necesitamos especificar las declaraciones de uni�n por defecto de element-ref para los elementos que preceden a este contenido; si no, el compilador de esquema no sabe qu� elementos se han pensado para dichas declaraciones de uni�n.
�Crear Interfaces
Puede ser que hayas notado que los elementos deposit, check, withdrawal tienen cierto contenido com�n. Puede que tambi�n hayas notado que cada uno representa una entrada en una lista de transacciones. Cuando tenemos un grupo de clases que proporcionan funciones similares y tienen alg�n comportamiento y propiedades comunes, podemos utilizar un interface para capturar las semejanzas entre las clases. En el caso de deposit, check, y withdrawal, todas tienen los elementos date y amount. Estos elementos ser�n propiedads comunes en el interface.
Para hacer que el compilador de esquema genere un inteface, con Deposit, Check, y Withdrawal implementaremos:
- En cualquier lugar del nivel m�s alto de nuestro esquema de uni�n, quiz�s despu�s de las declaraciones de enumeraci�n,
introducimos esta declaraci�n de interface:
<interface name=Entry members=Deposit Check Withdrawal properties=date amount />
El atributo members representa todas las clases que implementan el interface. El atributo properties representa el contenido com�n compartido por los miembros del interface.
Como los elementos deposit, check, withdrawal ocurren en el modelo contenido de las transacciones como un grupo de elecci�n, necesitamos referenciar Entry desde la uni�n del grupo de elecci�n. Previamente asignamos el nombre del interface al atributo de la propiedad y al valor list del atributo collection. Asignamos el nombre del interface al atributo supertype:
<element name=transactions type=class class=Transactions > <content> <choice property=entries collection=list supertype=Entry /> </content> </element>
El atributo supertype indica una clase o interface declarada en el esquema de uni�n que cada clase de elemento incl�ida en la propiedad choice implementa.
Estas declaraciones de uni�n producir�n un interface llamado Entry, que incluir� las propiedades date y amount:
public interface Entry { ... public int getAmount(); public void setAmount(int x); public Date getDate(); public void setDate(Date d);
�Manejar la Evoluci�n del Esquema
Como con cualquier recurso que genere c�digo, un desarrollador que escriba aplicaciones b�sadas en el c�digo necesita asegurarse de que el c�digo recientemente generado no rompa las aplicaciones. Si nos anticipamos a que el DTD cambiar�, podemos utilizar las declaraciones de uni�n m�s flexibles en el lenguaje de uni�n para proteger la integridad de las aplicaciones.
La manera m�s f�cil de manejar la evoluci�n del esquema es aceptar los uniones por defecto que el compilador de esquema produce. Estas uniones son definiciones muy flojas de las declaraciones del DTD, y as� son m�s flexibles a los cambios del DTD. Por ejemplo, cualquier grupo de modelo que no consista en elementos distintos en una secuencia no-repetitiva se unir� usando una declaraci�n de propiedad de contenido general, que une todo el contenido a una propiedad. Si agregaramos elementos a este grupo de modelo, estos elementos todav�a ser�an representados por la propiedad, y las clases no cambiar�an.
Podemos usar la declaraci�n de propiedad de contenido general para cualquier grupo de modelo. Para unir content usando esta declaraci�n, usamos la construcci�n content:
<content property=mygroup />
Esta uni�n gener� esta propiedad:
public List getMygroup(); public void deleteMygroup(); public void emptyMygroup();
Otra declaraci�n de uni�n que podemos utilizar para manejar la evoluci�n del esquema es la declaraci�n de uni�n rest. Esta declaraci�n de uni�n es similar a la declaraci�n de la propiedad de contenido general en que puede representar cualquier tipo de contenido. A�adiendo una declaraci�n rest sobre una declaraci�n de uni�n de elemento content, podemos a�adir otros elementos y grupos al modelo de contenido del DTD en el futuro sin afectar a las clases generadas. Por ejemplo, podemos a�adir una construcci�n rest a la uni�n de contenido del elemento withdrawal:
<content> ... <rest property=rest /> </content>
como ya tenemos la propiedad rest, podemos a�adir otro contenido al elemento withdrawal sin romper la aplicaci�n. Adem�s, todav�a podemos tener acceso individualmente al viejo contenido con las propiedads generadas por las otras declaraciones dentro de la declaraci�n de contenido. Solamente el nuevo contenido, definido por la propiedad rest, ser� representado por una propiedad List.
�Generar las Clases Java
Ahora que hemos terminado el esquema de uni�n, podemos ejecutar el compilador de esquema para generar las clases Java. Esta gu�a asume que hemos seguido las instrucciones en las notas de liberaci�n, situadas en el directorio doc de la instalaci�n y hemos configura los classpaths correctamente.
Para generar las clases Java:
- Ejecutamos el compilador de esquema con
checkook.dtd y
checkook.xjs, el esquema
de uni�n que hemos creado:
xjc checkook.dtd checkook.xjs
Ahora deber�amos ver los ficheros Checkbook.java y Entry.java en nuestro directorio actual.
- Compilamos los ficheros fuentes en clases Java:
javac *.java
El resto de esta secci�n explica el c�digo generado en estos ficheros. Si no necesitas una explicaci�n del c�digo, puedes ir al siguiente cap�tulo Construir Representaciones de Datos para construir �rboles de contenido usando las clases.
�Los Ficheros Fuente Java Generados
Esta secci�n explica abreviadamente algunos de los m�todos y de las clases p�blicas generadas por el compilador de esquema basadas en el DTD checkbook y el esquema de uni�n que creamos en la secci�n anterior. Puesto que las clases Deposit, Check, y Withdrawal son tan similares, entre estas clases, esta secci�n explica solamente la clase Check. Asimismo, esta secci�n explica solamente la clase enumerada CheckCategory, y entre las clases Pending, void, Cleared, esta secci�n explica solamente la clase pending.
�El fichero Checkbook.java
El elemento checkbook del DTD transactions.dtd est� unido a la clase Checkbook cuya firma es:
public class Checkbook extends MarshallableRootElement implements RootElement
Como checkbook es un elemento ra�z, la clase Checkbook extiende MarshallableRootElement, que es la clase que representa objetos elemento ra�z que pueden ser empaquetados y desempaquetados, e implementa RootElement.
Como cada clase generada, la clase Checkbook contiene un constructor sin argumentos:
public Checkbook();
Recuerda que el elemento checkbook contiene un elemento transactions y un elemento balance:
<!ELEMENT checkbook ( transactions, balance ) >
Estos elementos est�n unidos a �stas propiedades en el clase Checkbook:
// the transactions property public void setTransactions(Transactions x); public Transactions getTransactions(); // the balance property public void setBalance(java.math.BigDecimal x); public java.math.BigDecimal getBalance();
La propiedad transactions acepta y devuelve un objeto transactions porque el elemento transactions tambi�n est� representado por una clase. La propiedad balance acepta y devuelve un java.math.BigDecimal debido a �stas declaraciones de uni�n que especificamos en la secci�n Especificar Tipos:
<element name="balance" type="value" convert="BigDecimal"/> ... <conversion name="BigDecimal" type="java.math.BigDecimal" />
Aunque MarshallableRootElement define m�todos marshal, que la clase Checkbook usa por extensi�n, no define ning�n m�todo unmarshal. As�, el compilador de esquema genera estos m�todos unmarshal est�ticos en la clase Checkbook:
public static Checkbook unmarshal(InputStream in) public static Checkbook unmarshal(XMLScanner xs) public static Checkbook unmarshal(XMLScanner xs, Dispatcher d)
Cuando despempaquetamos un documento XML, podemos invocar a unmarshal(InputStream) o a unmarshal(XMLScanner) en los que InputStream o XMLScanner representan nuestro documento XML.
Aunque el desempaquetamiento realiza la validaci�n por nosotros, necesitamos realizar validaci�n despu�s de editar el �rbol de contenido y antes de empaquetarlo en un documento XML. Para este prop�sito, el compilador de esquema genera estos m�todos:
public void validateThis(); public void validate();
Despu�s de editar una parte del �rbol de contenido, podemos usar validateThis para validar el objeto editado. Antes de empaquetar el arbol de contenido a un documento XML, debemos usar validate para validar todos el �rbol de contenido.
�El Fichero Transactions.java
El elemento transactions de checkokk.dtd est� unido a la clase Transactions cuya firma es:
public class Transactions extends MarshallableRootElement implements RootElement
Al igual que checkbook , transactions es un elemento ra�z, y por eso la clase Transactions extiende MarshallableRootElement, e implementa RootElement. La clase Transactions contiene un constructor sin argumentos:
public Transactions();
El elemento transactions contiene cero o m�s elementos deposit, check, o withdrawal:
<!ELEMENT transactions (deposit | check | withdrawal)* >
Cuando seguimos las instrucciones de Personalizar las Declaraciones de Uni�n del Modelo de Contenido, especificamos que el compilador de esquema uniera el contenido del elemento transactions a una propiedad de colecci�n List llamada entries. En la secci�n Crear Interfaces, especificamos que el supertype de la propiedad entries es el interface Entry:
<element name=transactions type=class class=Transactions > <content> <choice property=entries collection=list supertype=Entry /> </content> </element>
El declaraci�n de uni�n del interface hizo que el compilador de esquema uniera el contenido de transactions a una lista de deposit, check, y withdrawals. La declaraci�n de uni�n del interface hizo que el compilador de esquema generara un interface Entry, que implementaban las clases Deposit, Check, y Withdrawal. El interface Entry se explica en la siguiente secci�n.
La propiedad entries consta de tres m�todos que usamos para aceder al contenido del elemento transactions:
public List getEntries(); public void deleteEntries(); public void emptyEntries();
El m�todo getEntry devuelve la lista de entradas completa. Una vez que consigamos la lista, podemos iterar a trav�s de la lista como lo har�amos con cualquier lista para obtener una entrada determinada. La lista devuelta por getEntries es modificable: Si cambiamos una entrada de esta lista, cambiar� la entrada en el �rbol de contenido. El m�todo deleteEntries suprime la lista de entradas actual. El m�todo emptyEntries suprime los valores de la lista.
Cualquier contenido de transactions (tanto si son dos dep�sitos o un dep�sito y cinco reintegros) implementa Entry. Por lo tanto, no necesitamos realizar pruebas de instanceof o forzados de tipo en los items de lista que representan entradas, a menos que estemos trabajando con un elemento contenido en deposit, check, o withdrawal que no sea un miembro del interface Entry.
Al igual que la clase Checkbook, la clase Transactions contiene los m�todos marshal, unmarshal y validate que un usuario deber�a invocar.
�El Fichero Entry.java
El supertype del contenido del elemento transactions es el inteface Entry, como se especifica en esta declaraci�n de uni�n de inteface:
<element name=transactions type=class class=Transactions > <content> <choice property=entries collection=list supertype=Entry /> </content> </element>
La firma del inteface Entry es:
public interface Entry {
El �nico contenido de elementos comunes entre los elementos deposit, check, y withdrawal son date y amount, y por eso asignamos estos elementos al atributo properties de la construcci�n del interface en la secci�n Crear Interfaces
<interface name=Entry members=Deposit Check Withdrawal properties=date amount />
Por lo tanto, el interface Entry, incluye las propiedades para los elementos date y amount:
public int getAmount(); public void setAmount(int x); public Date getDate(); public void setDate(Date d);
La propiedad amount devuelve y acepta un int porque escribimos una declaraci�n de conversi�n de uni�n para convertir desde String a BigDecimal y utilizamos el atributo convert en la declaraci�n de uni�n del elemento amount y le asignamos el valor BigDecimal:
<element name=amount type=value convert=BigDecimal /> <conversion name="BigDecimal" type="java.math.BigDecimal" />
�El Fichero Check.java
El elemento check est� unido a la clase Check, que tiene la firma:
public interface Check extends MarshallableObject implements Element, Entry{
La clase Check extiende MarshallableObject, que es la clase abstracta que representa cualquier objeto que se pueda empaquetar o despempaquetar pero no sea necesariamente un elemento ra�z. La clase Check debe implementar Element porque, al contrario que MarshallableRootElement, un MarshallableObject no tiene que ser un objeto derivado de un elemento y por lo tanto no implementa Element en si mismo. Finalmente, Check implementa Entry porque especificamos Entry como el supertype del contenido del elemento transaction en la secci�n Crear Interfaces. El elemento check tiene dos atributos y contiene seis elementos:
<!ELEMENT check ( date, name, (pending | void | cleared), memo? ) > <!ATTLIST check number CDATA #IMPLIED category ( rent | groceries | other ) #IMPLIED >
El elemento date est� unido a una propiedad y devuelve un objeto Date:
public Date getDate(); public void setDate(Date x);
Los elementos name, y memo, que s�lo contienen texto est�n unidos a estas propiedades:
public String getName(); public void setName(String x); public String getMemo(); public void setMemo(String x);
En la secci�n Especificar Tipos, especificamos un tipo int para el atributo number, produciendo esta propiedad:
public int getNumber(); public void setNumber(int x);
En la secci�n Personalizar Declaraciones de Uni�n de Modelos de Contenido especificamos que el modelo de grupo de elecci�n, (pending | void | cleared), est� unido a una propiedad, produciendo estos m�todos:
public MarshallableObject getPendVoidClrd(); public void setPendVoidClrd(MarshallableObject x);
La propiedad pend-void-clrd devuelve y acepta un MarshallableObject, que representa objetos que pueden ser empaquetados y desempaquetado. La raz�n por la que este tipo de propiedad no es un String es porque no podemos determinar si un String se supone que es un elmento pend, void, cleared; con MarshallableObject, si podemos porque el MarshallableObject ser� un objeto Pend, Void, o Cleared. La clase Check tambi�n contiene una propiedad para la enumeraci�n checkCategory, que especificamos en la secci�n Crear Tipos Enumerados:
<enumeration name=CheckCategory members=rent groceries other /> ... <attribute name=category convert=CheckCategory />
La propiedad que genera est� declaraci�n de uni�n es:
public CheckCategory getCheckCategory(); public void setCheckCategory(CheckCategory x);
Esta propiedad acepta y devuelve un objeto CheckCategory, que es un interface de la clase CheckCategory. Esta clase se explica en la siguiente secci�n.
�El Fichero CheckCategory.java
El atributo category toma un valor de un conjunto fijo de valores representado por un grupo de elecci�n en la definici�n de atributo:
<!ATTLIST check ... category ( rent | groceries | other ) #IMPLIED >
Cuando seguimos las instrucciones de la secci�n Crear Tipos Enumerados, especificamos una unid�n de este atributo a un tipo seguro enum:
<enumeration name=CheckCategory members=rent groceries other /> ... <attribute name=category convert=CheckCategory />
Un tipo seguro enum es una clase que representa una enumeraci�n que consta de un elemento o un atributo y la lista de los posibles valores, s�lo uno de los cuales puede asignarse a la clase. El nombre de la clase corresponde al del elemento o del atributo, y los campos est�ticos representan los valores. Las clases de tipo seguro enum tienen muchas ventajas, incluyendo controlar el tipo en tiempo de compilaci�n. El tipo seguro enum generado desde el atributo category es:
public final class CheckCategory { public final static CheckCategory RENT; public final static CheckCategory GROCERIES; public final static CheckCategory OTHER; public static CheckCategory parse(String x); public String toString(); }
El m�todo parse intenta mapear un argumento String a uno de los valores aceptados. El m�todo toString devuelve el valor actual de un CheckCategory como un String.
�El Fichero Pending.java
La clase Pending representa uno de los miembros del modelo de grupo de elecci�n contenido en la declaraci�n del elemento check:
<!ELEMENT check ( date, name, ( pending | void | cleared), memo? ) >
Este elemento, as� como los elementos void o cleared, est�n unidos a sus propias clases porque son parte de este grupo modelo de elecci�n. Seg�n lo explicado en el secci�n del fichero Check.java, la propiedad a la que est� unido este grupo debe devolver un MarshallableObject, que debe tambi�n ser un objeto Pend, Void o Cleared, de modo que la aplicaci�n sepa qu� elemento encontrar� durante el empaquetamiento o despempaquetamiento. Las clases Pend, Void y Clearedtienen un constructor sin argumentos.
El siguiente cap�tulo nos mostrar� como trabajar con estas clases para construir representaciones de datos.