Integración de XML y los JavaBeans

Si has experimentado con XML y JavaBeans en este tutorial, habr�s notado que la clase XMLBeanWriter que creamos en la p�gina anterior es capaz de crear ficheros XML desde XMLBeans que el XMLBeanReader de la primera p�gina no pod�a leer. Tambi�n habr�s observado que el formato del fichero XMLBeans es algo complicado, y que controlar qu� propiedades se codifican en XML y qu� propiedades son ignoradas requiere mucha codificaci�n aburrida. En esta p�gina, solucionaremos estos problemas reestructurando las clases XMLBeanReader y XMLBeanWriter. En el proceso, simplificaremos el formato del fichero mientras mantenemos la compatibilidad hacia atr�s. Quiz�s lo m�s interesante, simplificaremos y generalizaremos propiedades controladoras de XMLBeans integrando XMLBeans con el paquete b�sico java.beans.

.�Lecturas B�sicas

Est� p�gina cubre algunos t�picos intermedios-avanzados sobre JavaBeans, por eso podr�as necesitar algunas lecturas b�sicas para conseguirlos r�pidamente.

Primero, asuminos que has le�do y entendido las dos p�ginas anteriores. Si no las has le�do, est� p�gina no tendr� mucho sentido para t�.

.�Mejorar XMLBeans

El c�digo que vamos a discutir es una segunda tentativa de crear un interface para leer y escribir objetos JavaBeans como documentos XML. No gastaremos mucho tiempo en discutir las debilidades del dise�o anterior, lo veremos seg�n vayamos viendo que �sta nueva implementaci�n es m�s limpia y m�s flexible que la original.

Este p�gina presenta cuatro importantes mejoras al paquete XMLBeans. La primera implica la reestructuraci�n y simplificaci�n general del c�digo, aprovech�ndonos del concepto de que el valor de cualquier propiedad JavaBean puede ser un valor primitivo, un objeto, u otro JavaBean. La segunda mejora es una extensi�n personalizada de c�mo los XMLBeans pueden leer y escribir sus propiedades en XML (o, m�s exactamente, como �rboles DOM). Tercero, le damos al programador el control sobre la visibilidad y el comportamiento de la propiedades integrando el paquete XMLBeans con el paquete b�sico java.beans. Y finalmente, proporcionamos una alternativa, un formato menos restrictivo (y menos bien definido) para el XML producido y le�do por el paquete.

Empezaremos yendo sobre la mejora de la reestructuraci�n general.

.�Un Valor Mediante cualquier otro Nombre

La implementaci�n original de XMLBeans dibuj� una distinci�n entre las propiedades JavaBeans que son "valores" y las que son "beans". Los tipos de valor eran tipos primitivos de Java ( boolean, byte, char, short, int, long, float, o double) o cualquier tipo de objeto que no sea primitivo pero se pueda construir desde un String (java.net.URL, por ejemplo). Los tipos de bean fueron codificados expl�citamente en XML como <JavaBean CLASS="classname">, y las propiedades cuyos valores eran beans fueron procesadas de forma diferente a los que no eran.

Despu�s de escribir la primera versi�n del paquete XMLBeans, queda claro que no hay mucha diferencia entre un tipo de propiedad que sea un valor primitivo, uno que sea un objeto, o uno que sea un JavaBean. Los tipos primitivos requieren un tratamiento especial, �so es verdad, pero el proceso b�sico para leer un JavaBean en t�rminos de sus propiedades es simple:

  • Crear un ejemplar del JavaBean. El nombre de la clase del JavaBean aparece en el Element que representa el bean.

  • Por cada property Element (un Element que representa una propiedad del bean):
    • Determinar la clase de la propiedad (consult�ndole al PropertyDescriptor por la propiedad)

    • Crear un ejemplar de la clase de la propiedad (llamaremos a esto el propoerty-value) ya que ser� el valor que tendr� la propiedad.

    • Inicializar el ejemplar property-value usando los contenidos de la propiedad Element

    • Fijar la propiedad usando el ejemplar property-value y el accesor property-write (el setter)

Por eso, el proceso de escribir un JavaBean es sencillo:

  • Por cada propiedad del bean:

    • Obtenermos el valor de la propiedad desde el bean llamando al m�todo accesor lector de propiedades (el getter para la propiedad)

    • Convertimos el valor de la propiedad a un string

    • A�adimos un elemento que representa la propiedad y contiene su representaci�n string en el �rbol DOM que estamos creando

  • Cuando se han codificado todas las propiedades, escribimos el �rbol DOM como XML.

Observaremos que, en lo descrito arriba, se hace referencia al valor de la propiedad sin indicar si el valor es un bean, un valor primitivo, o un ejemplar de alguna clase no-bean. En el c�digo fuente (que discutiremos abajo), veremos que estos casos se manejan por separado y, en algunos casos, recursivamente. Pero la estructura b�sica del programa refleja el concepto de que el valor de una propiedad es simplemente un valor de propiedad, y las diferencias entre los distintos tipos de propiedades (valor, objeto, o bean) son s�lo detalles de programaci�n.

Ahora veremos la segunda mejora importante.

.�Personalizaci�n

Puesto que XMLBeans es un paquete de utilidad, lo que significa que puede ser utilizado por un programador, muchas de las caracter�sticas del paquete son facilidades de programaci�n, tales como funciones gancho que permitan que un programador modifique el comportamiento del paquete para requisitos particulares sin hacer que su operaci�n sea violenta.

Un ejemplo de dicho gancho es el m�todo getAsDOM(). En la P�gina anterior presentamos un m�todo de la clase XMLBeanWriter para controlar c�mo los tipos de propiedades espec�ficos y los JavaBeans se codificaban como �rboles DOM. El programador tiene la opci�n de especificar un m�todo en una clase del bean llamado getAsDOM(). Cuando XMLBeanWriter encuentra un JavaBean con el m�todo getAsDOM(), difiere al propio bean la capacidad para codificarse a s� mismo como un �rbol DOM. Si una clase JavaBean no define un m�todo getAsDOM(), el JavaBean se convierte a XML de la forma est�ndar: codificando todas sus propiedades en XML.

El m�todo DocumentFragment getAsDOM(Document d) por definici�n devuelve un �rbol DOM que representa su propio objeto. La clase XMLBeanWriter reconoce esta firma tanto para los beans como para las clases del tipo propiedad.

Como observamos arriba, si un JavaBean contiene un m�todo llamado getAsDOM() (con la firma y el tipo de retorno apropiados), XMLBeanWriter llamar� a ese m�todo y devolver� lo que devolvi� este m�todo como la representaci�n DOM del objeto. Si no existe getAsDOM(), XMLBeanWriter coloca todas las propiedades del bean, codificando cada propiedad como un �rbol DOM. Todo �rbol DOM que representa una propiedad se agrega a la lista de propiedades en el �rbol DOM que representa el bean, hasta que se codifican todas las propiedades. Adem�s de usar getAsDOM para codificar un bean entero, XMLBeanWriter siempre mira para ver si cada property type define getAsDOM() y, si lo hace, usa este m�todo para codificar la property como sub�rbol DOM.

Por ejemplo, imag�nemos que ten�amos una clase de bean llamada CustomerAccount, representando la informaci�n de un cliente determinado en un sistema de ventas en l�nea. El dise�ador de la clase CustomerAccount podr�a elegir especificar un m�todo llamado getAsDOM(). Cuando se aplica a un objeto CustomerAccount, XMLBeanWriter lo detectar� y llamar� al m�todo getAsDOM(). Entonces getAsDOM() devolver� la representaci�n DOM personalizada del JavaBean CustomerAccount, en vez de buscar todas las propiedades de CustomerAccount.

Adem�s, imag�nemos que CustomerAccount tiene una propiedad llamada Address, del tipo CustomerAddress. Si CustomerAccount no tiene un m�todo getAsDOM(), pero si lo tiene CustomerAddress, XMLBeanWriter codifica la propiedad Address llamando a CustomerAddress.getAsDOM().

Usar getAsDOM() en cualquiera de estos dos lugares le da al dise�ador del bean mucho control sobre c�mo se debe representar el bean cuando se convierte a XML. De hecho, en este punto el dise�ador tiene demasiado control porque la versi�n existente de XMLBeanReader no sabe manejar XML gen�rico. Como al final de la p�gina anterior, era posible escribir JavaBeans con XMLBeanWriter que no se podr�an leer con XMLBeanReader.

Por supuesto, la nueva versi�n del paquete XMLBeans elimina esta limitaci�n, permitiendo que la especificaci�n de un m�todo setAsDOM() se corresponda con cualquier m�todo getAsDOM(). Igual que como XMLBeanWriter puede utilizar getAsDOM() para extraer la representaci�n DOM de un bean (para escribir a un fichero XML), XMLBeanReader usa setAsDOM() para inicializar un valor de propiedad en un ejemplar del objeto bean desde la estructura de un sub-�rbol DOM.

Para proporcionar m�s flexibilidad, un programador de XMLBeans puede definir getDOMSetterName() y/o getDOMGetterName(), que deben devolver el nombre de un m�todo para usarlo en lugar de los nombres getAsDOM() o setAsDOM(). Esto permite m�s flexibilidad en el nombramiento, y tambi�n permite que el objeto decida en tiempo de ejecuci�n qu� m�todo utilizar para la traducci�n a XML. Esto podr�a ser �til en un caso en el que pudiera haber varias representaciones XML distintas de un objeto, requiriendo varias formas de getAsDOM() y de setAsDOM(), y la aplicaci�n necesita poder elegir la pareja de m�todos de traducci�n en tiempo de ejecuci�n.

Podemos recordar de la p�gina anterior que no era f�cil prevenir XMLBeanWriter para codificar propiedades. La �nica forma de hacer eso era escribir un m�todo getAsDOM() que codific� todas las propiedades como un �rbol DOM, incluyendo las propiedades no deseadas en la salida. Aunque esta t�cnica produce el resultado deseado, requiere mucha programaci�n innecesaria. La tercera mejora principal al paquete XMLBeans soluciona este problema (y otros) integrando los XMLBeans con el paquete java.beans.

.�Descriptores y Editores de Propiedades XML

T�picamente, un dise�ador de JavaBean controla la visibilidad de las propiedades del bean a los agentes externos creando un objeto BeanInfo para el bean. Uno de los m�todos del interface BeanInfo es PropertyDescriptor[ ] getPropertyDescriptors(), que devuelve un array de objetos PropertyDescriptor, uno por cada propiedad que defina el bean.

java.beans.Introspector, la clase responsable de analizar JavaBeans, identifica propiedades del bean intentando primero encontrar un objeto BeanInfo. Si encuentra uno, llama al m�todo getPropertyDescriptors() de BeanInfo para obtener la lista de propiedades. Solamente si esta tentativa falla hace que el propio Introspector analize la clase para determinar propiedades.

Por ejemplo, imaginemos un JavaBean ThingForSale con tres propiedades: int weight, int age, y int price. Un programador podr�a ocultar el atributo price simplemente definiendo un m�todo getPropertyDescriptors() en la clase BeanInfo para el bean ThingForSaleBeanInfo que devuelve objetos PropertyDescriptor s�lo para age y weight.

Un PropertyDescriptor no s�lo indica la propiedad, tambi�n los m�todos accesores para dicha propieddad (m�todos setter y getter). Como se describe arriba, cuando analizamos un bean, el Introspector primero chequea si puede obtener una lista de PropertyDescriptor desde el BeanInfo. Si esto falla, el Introspector analiza la clase bean, construyendo una lista de m�todos que coincidan con las firmas Type getProperty;() y void; setProperty(Type value). Desde la lista de m�todos, el Introspector crea su propia lista de PropertyDescriptor.

Si lo pensamos por un momento, deber�a estar claro que getAsDOM() y setAsDOM() en un JavaBean realmente podr�an usarse como los m�todos setter y getter para las propiedades del bean. En lugar de expresar las propiedades en t�rminos de su tipo real de propiedad, estos dos m�todos obtienen y fijan la propiedad en t�minos de un �rbol DOM. De hecho, toda propiedad del bean podr�a tener sus propios accesores setter y getter DOM para la propiedad cuyo valor sea un Element, y obtener el valor de la propiedad como un DocumentFragment, respectivamente. Incluso podemos definir una convenci�n de nombres para ellos, como podemos hacerlos para los accesores de propidades normales:

  • Un m�todo con la firma Type getPropertyAsDOM() define el "getter DOM" o el "m�todo de lectura DOM para una propiedad Property de tipo Type".

  • Un m�todo con la firma void setPropertyAsDOM() define el m�todo "setter DOM" o el "m�todo de escritura DOM para una propiedad Property del tipo Type.

Adem�s, tambi�n podemos crear una nueva subclase de java.beans.PropertyDescriptor, llamada com.javaworld.JavaBeans.XMLBeans.XMLPropertyDescriptor, que permite al programador definir los descriptores de propiedades XML desde dentro del marco de trabajo BeanInfo exitente. Como un XMLPropertyDescriptor es un PropertyDescriptor, funcionar� apropiadamente en cualquier papel de un PropertyDescriptor. Adem�s, cuando XMLBeanReader o XMLBeanWriter quieran conocer unas propiedades de JavaBean, simplemente pueden obtener la lista de propiedades desde el BeanInfo del bean. Cualquier PropertyDescriptor que sea un instanceof XMLPropertyDescriptor contendr� un m�todo "setter DOM" y/o un m�todo "getter DOM" para usarlos en la conversi�n de propiedades desde y hacia la representaci�n DOM.

Creando el nuevo tipo XMLPropertyDescriptor, permitimos a los desarrolladores de beans que especifiquen los m�todos usados para acceder a las propiedades como �rboles DOM. Si el desarrollador no proporciona un objeto BeanInfo, XMLBeanReader y XMLBeanWriter buscan los m�todos que coincidan con las convenciones de nombrado descritas arriba.

La segunda forma en la que el nuevo paquete XMLBeans se integra con java.beans es usar los editores de propiedades para las conversiones de tipos. El interface java.beans.PropertyEditor define tres m�todos de inter�s para nosotros:

  1. void setValue(Object o) selecciona los objetos a editar.

  2. void setAsText(String text) selecciona el valor del objeto actual, bas�ndose en los contneidos del String.

  3. String getAsText() codifica el objeto actual como un String y devuelve el resultado.

Estos m�todos nos interesan porque nos permiten convertir un objeto desde y hacia un String de una forma general, y en XML, todo se codifica como un String. Cuando XMLBeanReader determina que un string particular (el valor de un nodo Text) es una representaci�n string de una propiedad, construye un valor del tipo de la propiedad. Por ejemplo si un JavaBean tiene una propiedad Date BirthDate, XMLBeanReader construir� un objeto Date para pasarlo al m�todo setter de la propiedad BirthDate. Pero �C�mo convertir un String a un Date? Los objetos Date Java no tienen un constructor que acpeta un String como argumento. �Est�mos perdidos?

Por supuesto que no. Si existe un DateEditor, o si el PropertyDescriptor para el objeto Date especifica un editor personalizado , XMLBeanReader simplemente crea un editor de propiedades de la clase apropiada, maneja el objeto Date recientemente creado, y llama a setAsText(). XMLBeanReader difiere la conversi�n de objetos desde y hacia una representaci�n string al PropertyEditor de la propiedad, o (si la propiedad no tinen un editor personalizado), para el tipo de propiedad. Esta idea es extremadamente buena, porque le da al programador un completo control sobre el forma del string de cada propiedad, en una b�sica propiedad-por-propiedad. Un PropertyEditor personalizado podr�a estar escrito para codificar en base64 un objeto binario. Otro podr�a encriptar y desencriptar informaci�n sensible. La representaci�n texto de una propiedad ahora est� bajo el completo control del programador, y de una forma totalmente integrada con el coraz�n java.beans.

La mejora final de XMLBeans es una simplificaci�n opcional del formato de fichero.

.�Simplificador de XML

El formato de fichero para XMLBeans es preciso, pero no le permite f�cilmente procesar XML general. El lenguaje XMLBeans original constaba de un elemento <JavaBean>, con un s�lo atributo llamado CLASS. Dentro del elemento <JavaBean> hab�a un elemento <Properties>, que a su vez conten�a una lista de elementos <Property>. Cada elemento <Property> ten�a un atributo NAME, y conten�a un String, desde el que se podr�a construir la propiedad, u otro <JavaBean> (si el tipo de la propiedad era un JavaBean).

Es un lenguaje bastante sencillo. El DTD (data type definition) para este peque�o lenguaje aparece en el listado 1 de abajo:

<!ELEMENT JavaBean (Properties)>
<!ATTLIST JavaBean CLASS CDATA>
<!ELEMENT Properties (Property*)>
<!ELEMENT Property (#PCDATA)>
<!ATTLIST Property NAME CDATA>

Listado 1.

Este DTD le dice al analizador XML c�mo chequerar el XML del JavaBean para corregirlo antes de intentar procesarlo. Si usamos un analizador validante con este DTD, podemos estar seguro de que cualquier �rbol XML que leamos desde un fichero sigue las reglas de un XMLBean bien formateado.

El formato de fichero es un poco inflexible. Podr�amos haber notado que nadie excepto nosotros usa este dial�cto particular de "JavaBean XML." �Qu� pasa si queremos analizar cualquier documento XML y convertirlo en un JavaBean?

El nuevo paquete XMLBeans hace esto. Mientras que el lenguaje JavaBean XML definido arriba todav�a funciona, la clase XMLBeanReader ahora acepta cualquier XML bien-formateado, e intenta construir un bean de lo que encuentra. Hace esto asumiendo que:

  1. El elemento Element de m�s alto nivel de la entrada XML es un JavaBean, y que el nombre de la clase est� en la etiequeta del elemento o en el valor del atributo CLASS del elemento, si existe.

  2. Todo Element contenido dentro del Element de m�s alto nivel representa una propiedad del bean, cuyo nombre se da en la etiqueta del elemento.

  3. Si una propiedad de elemento (de la asumci�n dos de arriba) contiene un nodo Text no-vac�o, entonces el se puede construir el valor de la propiedad desde ese String.

  4. Si una propiedad de elemento (de la asumci�n dos) contiene otros Elements, entonces la propiedad es un JavaBean, y el valor es el resultado de cargar ese sub�rbol de elemento como un JavaBean

  5. La lista de propiedades podia estar encerrada opcionalmente en un elemento Properties, pero no necesariamente.

Por eso, por ejemplo, digamos que ten�amos una clase JavaBean llamada Player (conocida de las p�ginas anteriores). Algunas de las propiedades son JavaBeans, que tambi�n tienen propiedades. Las clases JavaBean y las propiedades en nuestro ejemplo son las siguientes:

Clase Propiedad Tipo de
Propiedad
Comentarios
Player name class PersonName

�

Player stats classStatistics

�

Player number int

�

Player grades class Grades

�

PersonName first String

�

PersonName last String

�

Statistics year int

�

Statistics atbats int

�

Statistics runs int

�

Statistics hits int

�

Statistics homeruns int

�

Statistics runsbattedin int

�

Statistics strikeouts int

�

Grade grade(i) String propiedad indexada
Grade classDesc(i) String propiedad indexada

En nuestro dialecto XML original, usando el DTD en el Listado 1, las primeras l�neas de un XMLBean se podr�an parecer al Listado 2:

<JavaBean CLASS="Player">
  <Properties>
    <Property NAME="Name">
      <JavaBean CLASS="PersonName">
        <Properties>
          <Property NAME="First">Benjamin</Property>
          <Property NAME="Last">Hur</Property>
        </Properties>
      </JavaBean>
    </Property>
    <Property NAME="Number">12</Property>
    <!-- and so on... -->
  </Properties>
</JavaBean>

Listado 2.

Codificar la misma informaci�n en el nuevo formato es mucho m�s claro, como en el Listado 3:

<Player>
  <Name CLASS="PersonName">
    <First>Benjamin</First>
    <Last>Hur</Last>
  </Name>
  <Number>12</Number>
  <!-- and so on... -->
</Player>

Listado 3.

Observa que se han eliminado casi todas las marcas especificas del lenguaje espec�fico XMLBeans. Lo �nico que queda es el atributo CLASS sobre la propiedad Name.

El listado 3 ejemplifica todas las siguientes reglas para la nueva s�ntaxis excepto la quinta:

  1. El elemento de m�s alto nivel es Player, por eso este objeto es un JavaBean de la clase Player. El nombre de la clase, Player, viene del nombre de la etiqueta.

  2. Player tiene las propiedades Name y Number (y probablemente algunas otras, no mostradas) ya que son elementos hijos del elemento Player.

  3. La propiedad Number contiene s�lo un elemento de texto no-vac�o, por eso su valor se crea usando el string "12" (y es convertido al tipo apropiado).

  4. La propiedad Name contiene otros Element s, por eso la propiedad Name es un JavaBean (cuya clase es PersonName, dada por el atributo CLASS).

  5. Todos los nodos dentro del nodo Player podr�an haber sido encerrados en un nodo <Properties>.

Un par de detalles menores sobre esta nueva s�ntaxis merecen una peque�a explicaci�n. Primero, �Por qu� mantenemos el atributo CLASS? �Por qu� no requerir s�lo que el nombre de la etiqueta sea el nombre de la clase? Hay dos razones para esta decisi�n. Primero, el atributo CLASS opcional proporciona una identificaci�n no ambig�a de la clase de la propiedad. �Qu� pasa si hubiera dos o m�s clases llamadas Player en nuestro CLASSPATH? �Cu�l deber�a elegir el XMLBeanReader? Segundo, y m�s importante, el atributo class puede usarse para identificar qu� subclase usar si un tipo de propiedad es abstracto. �Qu� pasar�a si PersonName fuera una clase base abstracta, con subclases de MarriedName, MaidenName, y NickName? �Qu� clase deber�a usar XMLBeanReader cuando cree un nuevo JavaBean?

El elemento <Properties> podr�a haber sido eliminado enteramente; y de hecho, es opcional. La raz�n por la que lo hemos dejado en la s�ntaxis es para futuras expansiones. Actualmente lo �nico almacenado sobre XMLBeans son las propiedades JavaBeans. En el futuro, podr�amos querer almacenar conjuntos de eventos, datos cacheados, firmas digitales, u otra informaci�n sobre el bean que no est� disponible desde las propiedades. El elemento <Properties> nos permite indicar qu� subnodos del bean son propiedades. En este ejemplo, no lo necesitamos porque todo lo que realmente procesamos es la lista de propiedades.

Ya hemos descrito las mejoras a XMLBeans. Ahora veamos algunos ejemplos.

.�Clases Beans de Ejemplo

Estos beans de ejemplo no forman parte del paquete XMLBeans; en vez de eso, se escribieron exclusivamente para demostrar c�mo XMLBeans podr�a operar sobre beans reales. Puesto que los hemos modificado para demostrar c�mo funciona la personalizaci�n de XMLBeans, pasaremos a trav�s de las clases y describiremos lo que hacen, y qu� caracter�sticas de personalizaci�n de XMLBeans utiliza. La clase bean Player del ejemplo representa a un miembro del equipo en un deporte de una High School; en este caso, b�isbol (porque el objeto Statistics codifica la estad�stica de b�isbol). El nombre de cada clase est� enlazado a un fichero fuente para esa clase.

Adem�s, hay ficheros XML de ejemplo, Bean1.xml y Bean2.xml, que muestran ejemplos de un bean Player codificado en el viejo y nuevo formatos XML. Podemos usarlos como entradas para los m�todos main() de las clases XMLBeans.

Clase Player
La clase Player representa un jugador de b�isbol. Tiene las siguientes propiedas:

  • PersonName name el nombre del jugador

  • Statistics stats las estad�sticas del jugador para este deporte

  • int number el n�mero del jugador en el equipo

  • String highSchool el nombre de la High School a la que asiste

  • Grades grades el expediente acad�mico del jugador

Las propiedades name, stats, y grades son todas JavaBeans , la propiedad number es un valor primitivo, y highSchool es un objeto String (un objeto no primitivo ni JavaBean). S�lo existen para demostrar c�mo XMLBeanWriter y XMLBeanReader codifican y decodifican cada tipo de propiedad.

Clase Statistics
Esta clase contiene las estad�sticas de b�sibol para el Player. Los nombres de las propiedades (year, atbats, runs, hits, homeruns, runsbattedin, y strikeouts) son todos del tipo int. Dos de ellas se manejan de forma especial:

  • hits Por ninguna raz�n en particular, hemos decidido codificar el n�mero de hits que recibi� el jugador en una sesi�n en formato hexadecimal. Las propiedades int nomalmente se codifican en decimal, pero getHitsAsDOM() y setHitsAsDOM() personalizan la codificaci�n de la propiedad hits del bean Statistics. Estos dos m�todos esencialmente permiten a la clase Statistics controlar c�mo se codifica y decodifcia la propiedad hits; en este caso, codifica y decodifica en hexadecimal. Cuando XMLBeanReader selecciona hits en el bean que est� creando, detecta la existencia de Statistics.setHitsAsDOM(), y le pasa el elemento correspondiente a ese m�todo en lugar de llamar al "setter" est�ndar. Igualmente, cuando XMLBeanWriter crea una representaci�n DOM de Statistics, nota que Statistics define getHitsAsDOM(), y difiere la codificaci�n de hits como un �rbol DOM a ese m�todo.

  • strikeouts Para proteger la privacidad de un atleta adolescente, hemos decidido encriptar la propiedad strikeouts. Si mir�mos el c�digo observaremos que la propiedad strikeouts no tiene unos m�todos accesores normales. En este sentido, no es realmante una propiedad!! Sin embargo, observaremos que el m�todo print() del objeto muestra el n�mero de strikeouts en su informe, por eso podemos ver que la cantidad fue correctamente decodificada. Los m�todos getEncryptedStrikeouts y setEncryptedStrikeouts obtienen y seleccionan la propiedad como un �rbol DOM, y encriptan y desencriptan la cantida, para que la gente extra�a que lea el XML no sepa lo que significa el valor de la propiedad.
    �C�mo XMLBeanReader y XMLBeanWriter saben usar este m�todo? Intentan obtener informaci�n de la clase StatisticsBeanInfo, que tienen un m�todo llamado getPropertyDescriptors(). El descriptor de propiedades para la propiedad strikeouts es un XMLPropertyDescriptor. Luego, la XMLPropertyDescriptor especifica que el "setter" del DOM para strikeouts es setEncryptedStrikeouts(), y el "getter" DOM para strikeouts es getEncryptedStrikeouts. XMLBeanReader y XMLBeanWriter usan el descriptor de propiedad devuelto por getPropertyDescriptors para determinar c�mo obtener y seleccionar las propiedades, y elige los accesores DOM sobre los accesores tradicionales cuando es posible.

Clase PersonName
La clase PersonName representa el nombre y los apellidos de una persona. No tiene personalizaci�n DOM y existe como un ejemplo de c�mo XMLBeans lee y escribe objetos que no saben nada de XMLBeans.

Clase Grades
La clase Grades representa un expedi�nte acad�mico de un Player. Particularmente son interesantes dos de sus propiedades classDesc y grade porque son propiedades indexadas, y XMLBeans actualmente no maneja propiedades indexadas. Para evitar esta limitaci�n, Grades define getAsDOM() y setAsDOM(), que son totalmente responsables de codificiar y decodificar todo el bean Grades como XML. Hay una tercera propiedad, gradePointAverage, que es interesante porque es de s�lo-lectura, y se calcula desde los valores de la lista de valores grade. Esta propiedad incluso no est� encerrada en el XML, porque eso ser�a redundante. Simplemente usa los valores de la propiedad grade para calcular la nota al estilo Americano (en un escada de 0 a 4).

Clase PlayerBeanInfo
La clase PlayerBeanInfo especifica las propiedades XMLBeansPackage del bean Player. Extiende la clase XMLBeans XMLSimpleBeanInfo. Muestra un ejemplo de un objeto BeanInfo est�ndar que especifica una lista de propieades tradicionales (es decir, no-XMLBeans).

Clase StatisticsBeanInfo
Otra subclase de XMLSimpleBeanInfo, StatisticsBeanInfo especifica propiedades Statistics. Lo interesante de esta es que, en getPropertyDescriptors(), especifica un XMLPropertyDescriptor. Puede que otros contenedores JavaBeans distintos de XMLBeans, y XMLPropertyDescriptor se parezcan exactamente como un PropertyDescriptor, por esta simple raz�n este es uno (su subclase). Pero suministra informaci�n adicional (los accesores DOM) al XMLBeans.

Ahora que hemos visto lo que hace la nueva implementaci�n, veamos c�mo trabaja.

.�Una Mirada al C�digo

El codigo de XMLBeanReader es el m�s d�ficil de los dos m�todos del paquete, por eso lo veremos primero.

.�XMLBeanReader

Veamos los m�todos de XMLBeanReader El m�todo main(), que no vamos a mostrar aqu�, simplemente pasa un nombre de fichero a readXMLBean(), y luego imprime el JavaBean resultante llamando al m�todo print() del objeto (si existe).

readXMLBean()
El m�todo readXMLBean() viene en tres sabores, s�lo uno hace realmente el trabajo (el que acepta un Reader como argumento). readXMLBean(), aparece en el listado 4, crea un analizador XML, lee el fichero XML de entrada, identifica el primer elemento del documento le�do, y llama a instantiateBean() para crear un ejemplar del bean e inicializarlo. Este m�todo analiza XML en un �rbol DOM llamando a un analziador. Aqu� usamos XML4J de IBM pero podr�amos haber elegido otro analizador y el cambio ser�a muy sencillo.

816/** Read a bean's state from a character stream
817 * @param r The Reader from which to read the
       * JavaBean's state.
818* @return Object The newly-created, 
      * initialized JavaBean.
819* @exception IOException
820* @exception ClassNotFoundException
821* @exception IntrospectionException
822*/
823public static Object readXMLBean(Reader r)
824     throws IOException, ClassNotFoundException, 
            IntrospectionException {
825 
826     // Read document from XML file
827     String sParserClassname = "";
828     Parser parser = null;
829 
830     // Create a SAX parser
831     try {
832         parser = ParserFactory.makeParser("com.ibm.xml.parsers.NonValidatingDOMParser");
833     } catch (Exception exc) {
834         System.err.println("Exception");
835         exc.printStackTrace();
836     }
837
838     P("Created parser");
839 
840     // Run the SAX parser against the input stream
841     try {
842         parser.parse(new org.xml.sax.InputSource(r));
843      } catch (SAXException sx) {
844         System.err.println("Exception: "  + sx.toString());
845         sx.printStackTrace();
846     } catch (Exception ex) {
847         System.err.println("Threw " + ex.getClass().getName());
848         ex.printStackTrace();
849     }
850
851     // Since we know the SAX parser is also 
           // a DOM parser,
852     // we can ask it for its resulting document.
853     Document d = ((NonValidatingDOMParser)parser).getDocument();
854     P("Got document " + isNull(d));
855 
856     Element eJavaBean = d.getDocumentElement();
857     P("eJavaBean is " + isNull(eJavaBean));
858
859     Object o = instantiateBean(eJavaBean);
860 
861     return o;
862 }

Listado 4.

instantiateBean()
El m�todo instantiateBean() (listado 5) simplemente obtiene el nombre de la clase del Bean y luego carga el bean como si fuera un valor de propiedad y otro bean llamando a loadValue(), el m�todo real del paquete.

477/** Instantiate the JavaBean, and set all of 
       * its properties
478 * The element must be of type "JavaBean"
479 * @param eJavaBean The DOM Element object 
       * representing the JavaBean.
480 * It is the root of a DOM document tree that 
       * contains the information
481 * used to instantiate and initialize the 
       * JavaBean. The element's
482 * tag must be JavaBean.
483 * @return java.lang.Object
484 * @exception IOException
485 * @exception ClassNotFoundException
486 * @exception IntrospectionException
487 */
488 protected static Object instantiateBean( Element eJavaBean)
489     throws IOException, ClassNotFoundException, 
            IntrospectionException {
490 
491     // Load the value from the node
492     Object jb = loadValue(classOfBean,  eJavaBean, null);
494 
495     // Return the newly-instantiated JavaBean. 
496     return jb;
497 }

Listado 5.

loadValue()
El m�todo loadValue() (Listado 6) es responsable de cargar todas las propiedades del JavaBean cuya clase recibe. Siempre recibe la clase del objeto a crear y un Element qiue es la ra�z de un sub�rbol DOM que codifica el valor. Tambi�n hay un PropertyDescriptor que podr�a contener informaci�n sobre el valor y c�mo seleccionarlo. Si el valor a devolver no es una propiedad de cualquier otro objeto (que es el caso s�lo si es el JavaBean de m�s alto nivel que est� siendo codificado, ver la llamada en instantiateBean() arriba), el PropertyDescriptor es null.

565 /**
566  * Load a value from an element node. The
        * resulting value will either be a
567  * JavaBean or a primitive wrapper object;
        * in either case, the result is almost
568  * always passed to a setter method. If the PropertyDescriptor is not null,
569  * then this object is a property of another
        * object, and the descriptor describes it.
570  * @return boolean
571  * @param bean java.lang.Object
572  * @param node org.w3c.dom.Node
573  */
574 public static Object loadValue( Class objectType, Element element, PropertyDescriptor pd) {
575     Object value = null;
576     Pe("Loading element " + element.getTagName()  + " for object "  + objectType.getName());
577 
578     // If this is a primitive property, we want to
           // return an object
579     // of the appropriate type for the property
           // descriptor's setter method
580     if (pd != null) {
581         if (objectType.isPrimitive()) {
582             value = getPrimitive(objectType, element);
583             return value;
584         }
585     }
586 
587     // Create an instance of this bean. This 
           // will be our return value.
588     try {
589         value = Beans.instantiate(null,  objectType.getName());
590     } catch (Exception ex) {
591         return null;
592     }
593 
594     // First, find out if this object knows how to
           // read itself
595     // from a DOM tree. If it does, we simply
           // defer to the object
596     // itself, and we have no further work to do.
597     Method domSetter = getDOMSetter(value, null);
598     if (domSetter != null) {
599         try {
600             domSetter.invoke(value, new Object[] {element});
601         } catch (Exception ex) {
602             Pe("loadValue:");
603             Pe(ex.getClass().getName());
604             ex.printStackTrace();
605         }
606         return value;
607     }
608 
609     // Get the first child of the element that
           // is either nonwhite text (in
610     // which case we try to initialize the
           // value from that); or a noncomment
611     // nonterminal node, in which case the value
           // must be a bean.
612     Node n =Util.getFirstInterestingChild(element);
613 
614     // If the syntax is <
           // Property NAME="foo"><
           // JavaBean CLASS="bar">...,
615     // then we want to look under the <
           // JavaBean> node for properties,
616     // not under the <Property> node.
617     Element topElement = element;
618     if (n instanceof Element && ((Element)n).getTagName().equals("JavaBean")) {
619         topElement = (Element)n;
620     }
621 
622     // If n is null, initialize value with a
           // blank string.
623     // If n is Text, initialize value with the 
           // value of the text.
624     String scalarValue = null;
625     if (n == null) {
626         scalarValue = "";
627     } else {
628         if (n instanceof Text) {
629             scalarValue = ((Text) n).getData();
630         }
631     }
632 
633     // If scalarValue is not null, then we want to
           // set the value from
634     // a string. This makes it easy for objects to
           // simply serialize
635     // themselves to flat strings, and then
           // deserialize themselves
636     // back from those strings. Essentially, the
           // PropertyEditor works
637     // as a tiny parser for the string value.
638     if (scalarValue != null && pd != null) {
639         // Use the property editor to initialize
               // the value.
640         if ((value = loadValue(objectType, scalarValue, pd)) != null) {
641             return value;
642         }
643         Pe("loadValue() failed for property " + pd.getName() + ", value = '" + scalarValue + "'");
644     }
645 
646     // Since the object didn't know how to set
           // itself from a DOM,
647     // and the DOM that represents it contains
           // multiple nodes, we're
648     // going to have to enumerate all of the
           // properties. Assume that
649     // each subnode is a property, creating and
           // initializing a value for
650     // each one, and then using the property 
           // setter to set that value.
651     // If a <Properties> node exists in my
           // subnodes, that's the list of
652     // my properties, instead of all
           // of my subnodes.
653     Node propertyList = topElement;
654     NodeList nl = topElement.getChildNodes();
655     int i;
656     for (i = 0; i < nl.getLength(); i++) {
657         if (nl.item(i) instanceof Element) {
658             Element e = (Element) (nl.item(i));
659             if (e.getTagName().equals("Properties")) {
660                 propertyList = e;
661                 break;
662             }
663         }
664     }
665 
666     // Introspect object to get properties, then
           // create a hash table
667     // with property names as keys and descriptors
           // as contents
668     // Use the introspector to get the property
           // descriptors
669     // for this bean.
670     PropertyDescriptor[] pds = getPropertyDescriptors(value.getClass());
671 
672     // Create a hash table of property names
           // and property descriptors
673     Hashtable htpd = new Hashtable();
674     for (i = 0; i < pds.length; i++) {
675         htpd.put(pds[i].getName().toLowerCase(), pds[i]);
676     }
677 
678     // Now iterate through all of the properties
           // in the propertyList,
679     // loading each one recursively, and
           // passing the returned value
680     // to the setter method.
681     nl = propertyList.getChildNodes();
682     for (i = 0; i < nl.getLength(); i++) {
683 
684         // Get the name of the property,
               // ignoring rubbish
685         String propertyName =getPropertyName(nl.item(i));
686         if (propertyName == null)
687             continue;
688 
689         p("Property name = '"  + propertyName + "'");
690 
691         // Must be an element, since it wouldn't 
               // have a propertyName if not
692         Element e = (Element) (nl.item(i));
693 
694         // Get the property descriptor for the property,
               // ignoring
695         // if there's no descriptor for it
696         PropertyDescriptor thispd = (PropertyDescriptor) (htpd.get(propertyName.toLowerCase()));
697         if (thispd == null) {
698             Pe("Couldn't get PropertyDescriptor for property " + propertyName);
699             continue;
700         }
701 
702         // See if either the property descriptor or 
               // the bean knows how
703         // to set the value as a DOM tree
704         Method mDOMSetter = getDOMSetter(value, thispd);
705 
706         if (mDOMSetter != null) {
707             P(" DOM Setter = "+ mDOMSetter.getName());
708             try {
709                 mDOMSetter.invoke(value, new Object[] {e});
710                 continue;
711             } catch (Exception ee) {
712                 Pe("Exception occurred while setting " + propertyName + " as DOM: " + ee.getMessage());
713                 ee.printStackTrace();
714                 return null;
715             }
716         } else {
717             p(", no DOM setter");
718         }
719 
720         // Call the setter directly. To do this, 
               // we have to get the setter method for
721         // the property, create an instance of its 
               // property type, and
722         // try to recursively loadValue() a value for it.
723         // That should handle both properties that are
               // JavaBeans
724         // and properties that can somehow be 
               // constructed from a string.
725         Method mSetter = thispd.getWriteMethod();
726         if (mSetter != null) {
727             P(", Setter " + mSetter.getName());
728             try {
729                 // Get the class of the property
730                 Class cValue = getPropertyType(e, thispd);
731                 Object setterArg = null;
732 
733                 // The setter argument is the object 
                       // referred to by the
734                 // current element e. Calling loadValue() 
                       // recursively here
735                 // returns the argument for the setter.
736                 if ((setterArg = loadValue(cValue, e, thispd)) != null) {
737                     mSetter.invoke(value, new Object[] {setterArg});
738                 } else {
739                     Pe("Couldn't set " + cValue.getName() + " " + thispd.getName());
740                 }
741             } catch (Exception ex) {
742                 Pe("Couldn't create or set "  + thispd.getPropertyType().getName() 
                             + " for property " + pd.getName());
743                 ex.printStackTrace();
744             }
745         } else {
746             P(", Setter null");
747         }
748     }
749 
750     // Return the value we've just created.
751     return value;
752 }

Listado 6.

Refierendose al c�digo, loadValue() realiza las siguientes tareas:

L�neas Descripci�n
578-585 getPrimitive() codifica una propiedad primitiva (v�ase abajo). Una propiedad que no sea primitiva debe tener un constructor por defecto, as� que usamos java.beans.Beans.instantiate() para crear un ejemplar para el resultado. (Usar este m�todo no es enteramente correcto, puesto que el resultado puede ser un objeto no-bean. El c�digo debe controlar si la clase del resultado es un bean o no, y usar new si no lo es.
588-591 Crear el "value" para ser incializado desde Element. El valor es simplemente un ejemplar del tipo de retorno.
594-607 Si el objeto conoce c�mo inicializarse a s� mismo desde un �rbol DOM (es decir, si la clase valor define setAsDOM()), simplemente pasa el �rbol DOM al m�todo "setter" DOM y se crea el Bean. El m�todo getDOMSetter() devuelve un objeto Method que representa al "setter" DOM para la propiedad, o null si no se pudo encontrar ninguno. Si se encontr� un "setter" DOM, esta secci�n lo llama y retorna. getDOMSetter() (ver el fichero fuente) encuentra un m�todo de una de estas formas: (1) si pd es un XMLPropertyDescriptor, devuelve el "setter" DOM desde el descriptor (llamando al m�todo getDOMWriteMethod() del descriptor); o (2) si la clase del valor define getPropertynameAsDOM(), se devuelve ese m�todo.
609-620 Los contenidos del nodo valor podr�an ser una de dos cosas. Podia ser una simple cadena de texto, en cuyo caso, el valor de la propiedad podr�a construirse desde un string como este: <Size>12</Size> o, podr�an ser otros nodos Element definiendo una lista de propiedades, como esta: <Size>12</Size> <Color>Red</Color> Los espacios en blanco mostrados en la entrada crean nodos Text que contiene espacios en blanco, y tambi�n podr�an ser nodos Comment que son saltados en el XML . getFirstInterestingChild() es un m�todo de utilidad que encuentra el primer hijo que es un nodo Text no vac�o, o cualquier otro tipo de nodo que no sea Comment.
622-644 Si el objeto que estamos creando va a ser inicializado desde un nodo Text (es decir, un String), llamamos desde otra forma de loadValue() que est� especializada en la inicializaci�n de un objeto desde un String:
666-752 Este c�digo es el m�todo est�ndar para crear XMLBean en ausencia de cualquier personalizaci�n. El objeto que est� siendo creado es inspeccionado para conseguir una lista de sus propiedades, y entonces se crea una tabla que indexa los nombres de propiedades contra sus correspondientes descriptores. (Estas son las propiedades del valor creado.) Puesto que cada hijo del Element actual es una propiedad del valor que es siendo creado, el c�digo busca entre todos los elementos hijos de este nodo, obteniendo un valor para cada uno (mediante una llamada recursiva a loadValue()), y despu�s fij�ndolo con el m�todo "setter" de esa propiedad, llamado sobre el ejemplar que estamos creando. Es un poco confuso al principio. Simplemente estamos llamando a loadValue() recursivamente para conseguir el valor de cada propiedad de valor que est� siendo creada actualmente. El bean Player, por ejemplo, necesita llamar a loadValue() para conseguir los valores para sus propiedades stats, grades y name, y utiliza loadValue() para hacerlo.

loadValue(), de nuevo
Las l�neas 526-563 en XMLBeanReader, s�lo concierne a la creacci�n de aquellos valores que pueden ser construidos desde objeto String. Crea un nuevo ejemplar de la clase valor, usa el PropertyEditor de la clase valor para inicializar el valor, y devolver el resultado.

getPrimitive()
Los tipos primitivos son un poco complicados porque necesitan una clase envoltura para pasarlos a un objeto Method (que es nuestra �nica forma de acceder al "setter"). getPrimitive() acepta el objeto Class primitivo y construye un objeto envoltura apropiado usando el String del �rbol DOM para inicializar el valor. La envoltura la crea getPrimitiveSetterArg(), que es interesante si queremos ver c�mo construir un objeto cuando incluso no conocemos la clase del objeto en el momento de la ejecuci�n: simplemente obtenemos el constructor y lo llamamos!

Los otros m�todos de la clase XMLBeanReader son simples utilidades. Entendiendo la primera forma de loadValue(), no tendremos problemas en entender c�mo funciona el resto de la clase.

.�XMLBeanWriter

XMLBeanWriter crea un �rbol DOM que representa un JavaBean. El �rbol DOM resultante puede luego ser escrito en un fichero XML. XMLBeanWriter es m�s simple que XMLBeanReader. El �nico m�todo que hace alg�n trabajo interesante es getAsDOM(), que viene en dos formas: una para un bean, y otra para las propiedades de un bean.

getAsDOM()
Listado 7 contiene el m�todo getAsDOM() usado para beans enteros. La otra versi�n de getAsDOM() devuelve un DocumentFragment que codifica una propiedad y es usado por la primera versi�n. En el listado 8 podr�s encontrar una descripci�n de la segunda forma de getAsDOM().

022 /**
023  * Build a DOM DocumentFragment representing 
        * the class and properties of
024  * a JavaBean.
025  * @return org.w3c.dom.DocumentFragment - 
        * the document representing the JavaBean.
026  * @param doc Creates nodes using this 
        * document as a factory.
027  * @param bean The JavaBean for which we 
        * want the DOM tree
028  * @exception java.beans.IntrospectionException 
        * An error occurred during
029 * introspection of the JavaBean.
030 */
031 public static DocumentFragment getAsDOM(Document doc, Object bean) throws 
                IntrospectionException, InstantiationException,  IllegalAccessException {
032 
033 	// Create the fragment we'll return
034 	DocumentFragment dfResult = null;
035 
036 	// Analyze the bean
037 	Class classOfBean = bean.getClass();
038 
039 	// See if the bean has a custom name for its 
              // DOM setter
040 	String nameOfGetter = "getAsDOM";
041 	try {
042 	Method mNameGetter = classOfBean.getMethod("getDOMGetterName",new Class[] {});
043        nameOfGetter = (String) (mNameGetter.invoke(bean, new Object[] {}));
044 	} catch (Exception ex) {
045 	; // Ignore exceptionsthis 
              // either works or it doesn't
046 	}
047 
048 	// If the bean knows how to encode itself 
              // in XML, then
049 	// use the DOM document it returns.
050 	Method mGetAsDOM = null;
051 	try {
052 	mGetAsDOM =classOfBean.getMethod(nameOfGetter,new Class[] {org.w3c.dom.Document.class });
053 		if (mGetAsDOM != null) {
054 			dfResult =(DocumentFragment) (mGetAsDOM.invoke(bean,new Object[] {doc}));
055 			return dfResult;
056 		}
057 	} catch (Exception e) {
058 		; // Ignore exceptions
059 	}
060 
061     // If we found a DOM read method, invoke it,
           // and we're done.
062     if (mGetAsDOM != null) {
063         try {
064             dfResult = (DocumentFragment) mGetAsDOM.invoke(bean, new Object[] {doc});
065             return dfResult;
066         } catch (Exception e) {
067 	; // Ignore exceptions
068 	}
069}
070 
071     // Get a BeanInfo for the bean.
072     BeanInfo bi =  Introspector.getBeanInfo(classOfBean);
073 
074     // If the bean doesn't know how to encode
           // itself in XML,
075     // then create a DOM document by
           // introspecting the bean and
076     // inserting nodes that represent the
           // bean's properties.
077     if (dfResult == null) {
078         dfResult = doc.createDocumentFragment();
079         PropertyDescriptor[] pds = bi.getPropertyDescriptors();
080 
081         // Add an Element indicating that this
               // is a JavaBean.
082         // The element has a single attribute, 
               // which is that Element's class.
083         Element eBean =  doc.createElement("JavaBean");
084         dfResult.appendChild(eBean);
085         eBean.setAttribute("CLASS", classOfBean.getName());
086         Element eProperties = doc.createElement("Properties");
087         eBean.appendChild(eProperties);
088 
089         // For each property of the bean, get a
               // DocumentFragment that
090         // represents the individual property.
               // Append that DocumentFragment
091         // to the Properties element of the document
092         for (int i = 0; i < pds.length; i++) {
093             PropertyDescriptor pd = pds[i];
094             DocumentFragment df = getAsDOM(doc, bean, pd);
095             if (df != null) {
096 	        // Create a Property element and add to
                      // Properties element
097 	        Element eProperty =doc.createElement("Property");
098                eProperties.appendChild(eProperty);
099 
100 	        // Create NAME attribute, add it to
                      //Property element,
101 	        // and set it to name of property
102 	        eProperty.setAttribute("NAME", pd.getName());
103 
104                 // Append the DocumentFragment to the
                       //Property element
105 	         // This "splices" the entire
                       // DOM representation of the
106 	         // Property into the tree at this point.
107 	        eProperty.appendChild(df);
108 	        }
109         }
110     }
111     return dfResult;
112 }

Listado 7.

La versi�n bean de getAsDOM() codifica un JavaBean completo como un DocumentFragment DOM.

L�neas Descripci�n
33-69 getAsDOM() primero obtiene la clase del bean y determina si esa clase define un m�todo personalizado que codifique el bean como un �rbol DOM. Busca un m�todo cuyo nombre sea getAsDOM, a menos que la clase defina un m�todo String getDOMGetterName(), en cuyo caso este m�todo busca un m�todo con ese nombre. (Por favor, no confundamos el m�todo que vamos aqu�, XMLBeanWriter.getAsDOM(), y el m�todo que la clase del bean define para codificarse como un �rbol DOM.) Si se encuentra un m�todo "getter" DOM, se le llama, y lo que devuelve es el valor de retorno de este bean. Esto permite que un programador de beans tome un completo control sobre c�mo la clase del bean se representa como un �rbol DOM (y, por lo tanto, como un documento XML).
74-110 Si el bean no sabe c�mo codificarse a s� mismo como un �rbol DOM, getAsDOM codifica el bean creando un Element que representa el bean, y a�adiendo los subnodos que representan las propiedades del bean. La obtenci�n de las propiedades del bean como �rbol DOM implica la lectura de todas las propiedades del bean, la codificaci�n de cada propiedad como un sub�rbol DOM (representado por un DocumentFragment), y luego la inserci�n del sub�rbol de la propiedad en la lista de propiedades del bean. La l�nea 72 obtiene el objeto BeanInfo para el JavaBean desde el Introspector, y la l�nea 79 obtiene la lista de PropertyDescriptors que describe las propiedades del bean. Las l�neas 83 a 87 creean el nodo superior del �rbol DOM resultante. Las l�neas 92 a 110 pasan a trav�s de los PropertyDescriptor del bean, creando un Element por cada propiedad, codificando la propiedad del JavaBean como un sub�rbol (llamando a la segunda forma de getAsDOM(), e insertando el nuevo Element en la secci�n <Properties> del �rbol resultante. Cuando se hayan codificado y a�adido todas las propiedades al �rbol, el �rbol resultante vuelve a llamador en la forma de un objeto DocumentFragment (l�nea 111).

Esto es todo lo que hace la forma del bean getAsDOM(). La forma del getAsDOM() que codifica propiedades aparece en el listado 8:

113 /**
114  * Return a DOM DocumentFragment representing a
        * property of a JavaBean
115  * @return org.w3c.dom.DocumentFragment
116  * @param doc The document to use as a factory for
117  * subelements of the tree.
118  * @param bean The object that is the value of 
        * the property.
119  * This object must be a JavaBean.
120  * @param pd A property descriptor describing
121  * the property of which the object is a value.
122  * @exception IllegalAccessException
123  * @exception InstantiationException
124  * @exception IntrospectionException
125  */
126  public static DocumentFragment getAsDOM(Document doc, Object bean, 
                    PropertyDescriptor pd) throws IntrospectionException, 
                    InstantiationException, IllegalAccessException {
127 	Class classOfBean = bean.getClass();
128 	Class classOfProperty = pd.getPropertyType();
129 	DocumentFragment dfResult = null;
130 	String sValueAsText = null;
131 	Class[] paramsNone = {};
132 	Object[] argsNone = {};
133 
134 	// If the property is "class," and
              // the type is java.lang.class, then
135 	// this is the class of the bean, which 
              // we've already encoded.
136 	// So, in this special case, return null.
137 	if (pd.getName().equals("class") && classOfProperty.equals(java.lang.Class.class)) {
138 		return null;
139 	}
140 
141      // 0. If pd is an XML property descriptor, and
            // we can call its
142      // getter method, do so and return. If the
            // programmer
143     // specifies a DOM getter method, we return
           // what it returns, no questions asked.
144     if (pd instanceof XMLPropertyDescriptor) {
145         Method getter;
146         if ((getter = ((XMLPropertyDescriptor) pd).getDOMReadMethod()) != null) {
147 	    try {
148 	        Class[] params =  {org.w3c.dom.Document.class};
149 	        Object[] args = {doc};
150 	        dfResult = (DocumentFragment) (getter.invoke(bean, args));
151 	    } catch (Exception ee) {
152 	        ; // Ignore... couldn't get the method
153 	    }
154 	    return dfResult;
155 	 }
156     }
157 
158     // 1. Try to represent the property as XML.
159     // This bean may know how to describe itself,
           // or parts of
160     // itself, as XML. There are two possibilities:
161     // [a] The bean has a method called 
           // get<Propname>AsDOM()
162     // [b] The property class has a method called 
           // getAsDOM()
163     // We'll try both of these, and the first 
           // (if any) that
164     // works will be the DocumentFragment we want
           // to return.
165     // If none of these are true, then we try to
           // find the object's
166     // value as text
167 
168     // [1a] Does the bean have a method called
           // get<Propname>AsDOM()?
169     // Capitalize property name
170     StringBuffer sPropname = new StringBuffer(pd.getName());
171     char c = sPropname.charAt(0);
172     if (c >= 'a' && c <= 'z') {
173 		c += 'A' - 'a';
174     }
175     sPropname.setCharAt(0, c);
176     String sXMLGetterName = "get" + sPropname + "AsDOM";
177 
178     // If both of these methods succeed, then
           // dfResult will be set
179     // to non-null; that is, the method existed
           // and returned a
180     // DocumentFragment
181     try {
182         Class[] params = {org.w3c.dom.Document.class};
183         Method mXMLGetter = classOfBean.getMethod(sXMLGetterName, params);
184 	Object[] args = {doc};
185 	dfResult = (DocumentFragment) (mXMLGetter.invoke(bean, args));
186     } catch (Exception ee) {
187 	 ; // Ignore... couldn't get the method
188     }
189 
190     // Hereafter, we're trying to create a 
           // representation of the property
191     // based somehow on the property's value.
192     // The very first thing we need to do is get 
           // the value of the
193     // property as an object. If we can't do that,
           // we can get no
194     // representation of the property at all.
195     Object oPropertyValue = null;
196     try {
197         Method getter = pd.getReadMethod();
198         if (getter != null) {
199             oPropertyValue = getter.invoke(bean, argsNone);
200 	}
201     } catch (InvocationTargetException ex) {
202 	 ; // Couldn't get value. Probably 
                // should be an error.
203     }
204 
205     // [1b] If we don't have a DocumentFragment,
           // the previous block failed.
206     // So, let's find out if the property's 
           // class has a method called
207     // getAsDOM() and, if it does, call that instead.
208     if (dfResult == null) {
209         try {
210             Class[] params = {org.w3c.dom.Document.class};
211             Method mXMLGetter = classOfProperty.getMethod("getAsDOM", params);
212             Object[] args = {doc};
213             dfResult = (DocumentFragment) (mXMLGetter.invoke(oPropertyValue, args));
214         } catch (Exception ee) {
215 	    ; // Ignorewho cares why it failed?
216 	 }
217     }
218 
219 
220     // 2. Try to represent the property as a String.
221     // See if this property's value
222     // is something we can represent as Text, or if
           // it's something
223     // that must be represented as a JavaBean. 
           // Let's assume that this
224     // object can be represented as text if:
225     // [a] it has a PropertyEditor associated with
           // it, because
226     // PropertyEditors always have setAsText() and
           // getAsText()
227     // If it can't be represented as text, then 
           // we pass it to
228     // getAsDOM(Document, Object) and return 
           // the result.
229 
230     if (dfResult == null) {
231 
232         // [2a] Can we get either a custom or 
               // built-in property editor?
233         // If the PropertyDescriptor returns an 
               // editor class, we
234         // create an instance of it; otherwise, 
               // we ask the system for
235         // a default editor for that class.
236         Class pedClass = pd.getPropertyEditorClass();
237         PropertyEditor propEditor = null;
238         if (pedClass != null) {
239             propEditor = (PropertyEditor) (pedClass.newInstance());
240         } else {
241 	    propEditor = PropertyEditorManager.findEditor(classOfProperty);
242 	}
243 
244         // If the property editor's not null, pass
               // the property's
245         // value to the PropertyEditor, and then ask 
               // the PropertyEditor
246         // for a text representation of the object.
247         if (propEditor != null) {
248             propEditor.setValue(oPropertyValue);
249 	    sValueAsText = propEditor.getAsText();
250         }
251 
252         // If somewhere above we found a string value,
               // then create
253         // a DocumentFragment to return, and append 
               // to it
254         // a Text element.
255         if (sValueAsText != null) {
256             dfResult = doc.createDocumentFragment();
257 	    Text textValue = doc.createTextNode(sValueAsText);
258 	    dfResult.appendChild(textValue);
259         }
260     }
261 
262     // 3. Try to represent the property value as a
           // JavaBean
263     // If we don't have a DocumentFragment yet, we'll
264     // have to introspect the value of the object, because
265     // it's apparently something that can't be
           // represented
266     // as flat text. We'll assume it's a JavaBean.
267     // If it isn't... oh, well.
268     //
269     if (dfResult == null && oPropertyValue != null) {
270         dfResult = getAsDOM(doc, oPropertyValue);
271     }
272     return dfResult;
273 }

Listado 8.
L�neas Descripci�n
127-128 El m�todo primero obtiene la clase del bean, y la clase de la propiedad dentro del bean.
137-139 Algunas veces, el Class class muestra una lista de propiedades, en cuyo caso es ignorada.
141-156 Si el PropertyDescriptor tambi�n es un XMLPropertyDescriptor, usamos el m�todo read del DOM indicado por el XMLPropertyDescriptor para codificar la propiedad como un �rbol DOM.
158-188 Si la clase bean define un m�todo llamado getPropertynameAsDOM(), se utiliza un m�todo para recuperar la propiedad como un �rbol DOM.
190-203 Desde la l�nea 190, la �nica forma en la que podemos obtener una representaci�n DOM de una propiedad es obteniendo el valor de la propiedad, formatear ese valor como un String y embeber ese String dentro de un elemento <Property>. Las l�neas 190 a 203 recuperan el valor de la propiedad llamando al m�todo "getter" tradicional de la propiedad indicado por el PropertyDescriptor.
205-217 Si la clase property tiene un m�todo llamando getAsDOM(), llamamos a ese m�todo sobre el valor que acabamos de recuperar, y el resultado ser� la propiedad codificada como un �rbol DOM. Observa que esto es diferente a pedirle a la clase bean que codifique la propiedad como un �rbol DOM.
220-260 Si a�n no hemos tenido �xito en la creacci�n de un DocumentFragment para devolverlo, entonces tenemos que crear el nodo <Property> e insertarlo dentro del valor de la propiedad codificado como un String. Utilizamos el PropertyEditor del tipo de la propiedad para realizar esta conversi�n.
262-271 Si todo esto falla, usamos la primera verisi�n de getAsDOM() para codificar la propiedad como un JavaBean. Observa que esto significa que las dos versiones de getAsDOM() son mutuamente recursivas. Al final de esta secci�n de c�digo, el DocumentFragment resultante representa la propiedad devuelta al llamador.

.�Otras Clases XMLBeans

Estas clases en el paquete XMLBeans las usan los programadores que quieren a�adir funcionalidades XMLBeans a sus clases.

Clase XMLSimpleBeanInfo
La clase XMLSimpleBeanInfo existe s�lo para corregir un bug en la implementaci�n que IBM hace de java.beans.SimpleBeanInfo (que ha sucedido cuando lo usamos para desarrollar el c�digo de esta p�gina). XMLSimpleBeanInfo podr�a concebirse para extenderse y reportar m�s informaci�n sobre un XMLBean al paquete XMLBeans, pero hacer esto requiere el re-desarrollo (o al menos subclasificaci�n) de java.beans.Introspector, que podr�a ser un t�pico demasiado largo para esta p�gina.

Clase XMLPropertyDescriptor
La clase XMLPropertyDescriptor, es un descriptor de propiedad XML, extiende java.beans.PropertyDescriptor permitiendo al programador especificar los m�todos "setter" y "getter" DOM para cada propiedad: Al igual que con los objetos PropertyDescriptor tradicionales, los m�todos podr�an especifcarse por su nombre, o como objetos Method. XMLBeans busca los accesores DOM, y los usa preferiblemente sobre los accesores tradicionales, proporcionando, por lo tanto, la personalizaci�n XMLBeans descrita arriba. Esta clase probablemente podr�a haberse hecho m�s peque�a teniendo el introspector en la clase bean, buscando por getAsDOM(), etc., en lugar de mantener la l�gica en las clases XMLBeanReader y XMLBeanWriter; pero de nuevo, esto requerir�a subclasificar Introspector.

Clase Util
La clase Util contiene un conjunto de clases sencillas para codificaciones sencillas. Ninguna es lo suficientemene complicada como para merecer una explicaci�n aqu�.

.�Trabajo Posterior

XMLBeanReader y XMLBeanWriter son simplemente un punto de partida adicional para las posibilidades existentes de experimentaci�n despu�s de todo lo le�do hasta aqu�.

Como hemos mencionado en estas p�ginas, este paquete no maneja propiedades indexadas de una forma general, aunque las propiedades indexadas espec�ficas se pueden soportar si las codificamos a mano, como demuestra el ejemplo de Grades.

Hay algunas cosas menores que necesitamos a�adir a este paquete para poder hacerlo real. En particular, el paquete XMLBeans no codifica los caracteres (<, > y &) como deber�a. Cada vez que se crea un elemento Text los datos a�adidos al elemento Text deber�amos primero reemplazar cada caracter de mayor que (>) con la secuencia &gt;, cada caracter de menor que (<) con &lt;, y cada caracter ampersand con &amp;. Esta traduci�n tambi�n necesitamos hacerla a la inversa cuando leemos datos desde elementos Text. Estas codificaciones son requirimientos duros-y-r�pidos de XML, y son la mayor debilidad de este paquete en su implementaci�n actual.

Otra modificaci�n necesaria (quizas menor) es un cambio en el c�digo que maneja secciones CDATA, que son formas especiales de incluir grandes bloques de texto pre-formateado en un fichero XML. Las secciones CDATA deber�an permitirse en cualquier lugar en el que se permita un elemento Text.

Una �ltima forma para mejorar el paquete ser�a modificar el paquete XMLBeanWriter para crear ficheros XMLBeans usando el nuevo formato XMLBeans, en lugar de usar el viejo formato

Esta implementaci�n de XML JavaBeans realmente se precipita un poco cuando intenta convertir un objeto a un String. La �ltima cosa que el XMLBeanWriter deber�a hacer es comprobar si el objeto implements Serializable. Si lo hace, XMLBeanWriter podr�a serializar el objeto a un array de bytes, y luego codificar el array como un string BDC o base64. La operaci�n inversa tendr�a que ser realizada mientras se lee el JavaBean desde XML.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP