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)
- Determinar la clase de la propiedad (consult�ndole al PropertyDescriptor
por la propiedad)
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
- Obtenermos el valor de la propiedad desde el bean llamando al m�todo accesor lector de propiedades (el
getter para la propiedad)
- 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:
- void setValue(Object o) selecciona los objetos a editar.
- void setAsText(String text) selecciona el valor del objeto actual, bas�ndose en los
contneidos del String.
- 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:
- 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.
- 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.
- 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.
- 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
- 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:
- 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.
- Player tiene las propiedades Name y
Number (y probablemente algunas otras, no mostradas) ya que son elementos hijos del
elemento Player.
- 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).
- La propiedad Name contiene otros Element s, por eso
la propiedad Name es un JavaBean (cuya clase es
PersonName, dada por el atributo CLASS).
- 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 >, cada caracter de menor que (<) con <, y cada caracter ampersand con &. 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.