miércoles, 9 de febrero de 2011

La Crisis Crediticia Visualizada

Es probable que ya sepan muy buen todo lo relacionado con la crisis crediticia que nos afectó a nivel mundial no hace mucho tiempo... ¿o no?

Bueno espero que estos dos vídeos que les dejo a continuación les ayuden a entender, tal como me ayudaron a mi, porque es que se dio esta crisis...

La Crisis Crediticia Visualizada - Parte 1


La Crisis Crediticia Visualizada - Parte 2


Mi conclusión esto pasó porque el ser humano es demasiado avaricioso :S :(

Hasta la próxima.

miércoles, 2 de febrero de 2011

El poder de las expresiones regulares!

En el trabajo se presentó una excelente oportunidad de comprobar el poder de las expresiones regulares o RegEx cuando un compañero estaba buscando una forma de formatear números en javascript. Básicamente lo que se quería era poder separar con una coma "," los miles, por poner un ejemplo:
Dado el siguiente número:

ejemplo 1 - número a formatear
1000000.00
transformarlo en la siguiente cadena:

ejemplo 1 - número formateado
"1,000,000.00"

Encontramos las siguiente solución en StackOverflow

ejemplo 2 - solución usando regex en javascript
//fomat number using string.replace and regex
function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
} 

Esta sencilla y elegante solución me desconcertó e intrigó ¡no se me habría ocurrido! por lo que decidí investigar como funciona y aquí les dejo mi análisis y un port de ésto código a Java.

El análisis.
Primero veamos la definición de la función replace; pueden consultar otras funciones útiles que aceptan una regex como parámetro en javascript.

ejemplo 3 - definición de la función String.replace
/**
* Replaces matches with the given string, 
* and returns the edited string.
*/
String.replace(pattern, string)

Veamos el significado de la regex usada, como solía decir Fredcpp "vamos por partes":

expresiónsignificado
/marca el inicio y el fin de la expresión regular en javascript
()los paréntesis marcan el inicio y el fin de una supexpresión.
\B"non-word boundary" selecciona una palabra es decir un conjunto de caracteres rodeado por espacios en blanco.
(?=patrón), (?!patrón)"lookahead" prosigue solo si la expresión precedente es seguida por el patrón indicado

(?=patrón) captura solo si existe un patrón a continuación.
(?!patrón) captura solo si no existe un patrón a continuación.
(?:patrón)busca el patrón completo pero no lo captura.
\d{3}captura un patrón de 3 números seguidos, por ejemplo: 681
patrón+indica que el patrón se puede repetir una o muchas veces.
(?:\d{3})+captura un o varios grupos de 3 digitos
(?!\d)captura si el siguiente elemento NO es un número


Basándome en la tabla anterior, la expresión \B(?=(?:\d{3})+(?!\d)) captura todos los caracteres delante de uno o varios grupos de tres dígitos que no vienen seguidos de un número [1].

La solución
Antes de procesar el número debemos convertirlo en una cadena ("1000000.00").
Entonces se usa la expresión para romper el número en un arreglo que consta de los siguientes elementos: ["1", "000", "000.00"]. Luego es solo cuestión de unir las partes usando una coma y obtenemos: "1,000,000.00" (asumo que esto último es lo que hace internamente replace)

Potando el código a Java}
Java soporta las mismas expresiones usadas en javascript por lo que esta función se puede escribir en java con algunos pequeños ajustes, quedando de la siguiente forma:
//fomat number using String.replaceAll and regex
public String numberWithCommas(Number number) {
    return number.toString().replaceAll("\\B(?=(?:\\d{3})+(?!\\d))", ",");
}


Hasta la próxima
Notas:
1. Si alguien tiene una mejor interpretación por favor compartirla.

martes, 1 de febrero de 2011

Anotaciones en XStream


En un post anterior hablé sobre XStream. En este veremos como se pueden usar anotaciones en dicha librería.

XStream soporta anotaciones que hacen las cosas un poco más simples, pueden encontrar más información aquí.

Vamos a serguir trabajando con los POJOs del post anterior Person y PhoneNumber, pero ahora supongamos que el xml que debemos escribir tiene ciertas reglas, a saber:
  1. las etiquetas del xml para clase Person, cuando el nombre es una combinación de varias palabras, deben llevar separadas las palabras el caracter "_" sin las comillas.
  2. cuando un campo de tipo PhoneNumber sea serializado deberá llevar el atributo type con el valor "phone_number"

Vamos a usar la anotación XStreamAlias que se puede aplicar tanto a clases como a campos dentro de una clase y elimina la necesidad de crear los alias individualmente para cada clase y cada campo dentro de ellas.

ejemplo 1 - pojos usando anotaciones
public class PhoneNumber {
  private int code;
  private String number;
  // ... constructores y métodos
}

@XStreamAlias("person")
public class Person {
  @XStreamAlias("first_name") private String firstname;
  @XStreamAlias("last_name") private String lastname;
  private PhoneNumber phone;
  private PhoneNumber fax;
  // ... constructores y métodos
}

Por alguna razón que aun no termino de entender XStream reemplaza todos los "_" por "__" por lo cual debemos crear un nuevo XmlFriendlyReplacer.

ejemplo 2 - creando un nuevo XmlFriendlyReplacer
new XmlFriendlyReplacer("__", "_")

La mejor forma que encontré para agregar el atributo type de acuerdo a las reglas dadas es crear una implementación de XppDriver.

IMPORTANTE: debemos usar XStream con la librería XPP3.

ejemplo 3 - creando una implementación de XppDriver
new XppDriver(new XmlFriendlyReplacer("__", "_"))
{
  @Override
  public HierarchicalStreamWriter createWriter(Writer out) 
  {
    return new PrettyPrintWriter(out, this.xmlFriendlyReplacer())
    {
      
      @Override
      public void startNode(String name, Class type) 
      {
        super.startNode(name, type);
        if (PhoneNumber.class.isAssignableFrom(type)) {
          addAttribute("type", "phone_number");
        }
      }
    };
  }
}

Ahora ponemos todo junto y obtenemos lo siguiente.

ejemplo 4 - usando XStream con anotaciones y un nuevo XppDriver
XStream xstream = new XStream(new XppDriver(new XmlFriendlyReplacer("__", "_"))
{
  @Override
  public HierarchicalStreamWriter createWriter(Writer out) 
  {
    return new PrettyPrintWriter(out, this.xmlFriendlyReplacer())
    {
      
      @Override
      public void startNode(String name, Class type) 
      {
        super.startNode(name, type);
        if (PhoneNumber.class.isAssignableFrom(type)) {
          addAttribute("type", "phone_number");
        }
      }
    };
  }
});
xstream.processAnnotations(new Class[]{Person.class});//[1] procesa todas las anotaciones
xstream.autodetectAnnotations(true);//[2] habilita autodeteción de clases anotadas.

Person joe = new Person("Joe", "Walnes");
joe.setPhone(new PhoneNumber(123, "1234-456"));
joe.setFax(new PhoneNumber(123, "9999-999"));
String xml = xstream.toXML(joe);

Si revisamos el contenido de la variable xml veremos que se ha generado un xml con las condiciones indicadas al inicio de este post.

ejemplo 5 - contenido de la variable xml

  Joe
  Walnes
  
    123
    1234-456
  
  
    123
    9999-999
  



Hasta la próxima. :D

[Notas]
1 Las anotaciones tienen la desventaja de que es necesario procesarlas antes de poder leer un archivo XML; Con processAnnotations nos aseguramos las clases pasadas como parámetro están listas para usarse tanto para leer como para escribir xml.

2 autodetectAnnotations le permite a XStream autodetectar clases anotadas y automatizar su procesamiento pero debemos recordar que llamar al método processAnnotations desactiva esta funcionalidad.