Wednesday, August 30, 2006

[JAVA] Obtener el classpath en tiempo de ejecución

Cuando trabajas en java se maneja el concepto de classpath, entendamos primero el concepto antes de entrar al tema:

El classpath es el conjunto de rutas (ubicaciones) a las que tiene acceso el sistema operativo (p.ej: 'C:\Documents and settings\Mi Sesión') y donde el JVM (java virtual machine) puede encontrar byte code (.class que son los generados al compilar fuentes de java). La funcionalidad es similar a la variable de entorno 'PATH' de windows y linux.

Al ejecutar una aplicación de java es común encontrar líneas de ejecución como esta:

java -classpath IconosSC.jar;bin\OtroJar.jar; Paquete.Main

donde se está agregando al classpath del JVM el jar IconosSC.jar, el jar dentro de la carpeta bin con nombre OtroJar.jar y finalmente el nombre de la clase que iniciará la ejecución del programa que lleva el nombre de Main.

Ahora, que ocurre si queremos ver los directorios del classpath en tiempo de ejecución?, simple: Existe una clase estática llamada: ClassLoader, el concepto de cargador de clases puede dar tema a otro tópico para el blog, pero por el momento diremos que es el encargado de levantar las clases en el JVM, veamos las siguientes instrucciones:

import java.net.*;
URLClassLoader Cargador = (URLClassLoader)ClassLoader.getSystemClassLoader();
URL[] cjto = Cargador.getURLs();
System.err.println("Classpath size: " + cjto.length);
for (int i=0; i < cjto.length; i++)

System.err.println(i + " " + cjto[i].toString());


Las clases utilizadas se encuentran en java.net, como veras, el ClassLoader nos permite obtener el cargador de clases del sistema, devolviendonos una instancia de URLClassLoader a través del método 'getSystemClassLoader', luego podemos obtener los URL's (path's) en objetos URL que también se encuentran en java.net. Esto nos da muchas posibilidades si lo que buscamos es trabajar con el classpath, como por ejemplo si quisieramos agregar un jar en tiempo de ejecución, pero eso será otro tema para el blog!.

Saturday, August 26, 2006

[ACCESS] Calculando la edad de una persona

En la generación de reportes, si éste muestra la información de personas, es común que se necesite mostrar su edad en años, el inconveniente aquí es que las bases de dato no contienen una función nativa que nos permita realizar esta tarea (p.ej: CalculaEdad(parámetros)).

Ahora, las funciones que nos proporciona MS Access para trabajar con los tipos de dato DateTime (Fecha/Hora) nos serán de utilidad:

DatePart(...,CampoFecha) nos devuelve un elemento del CampoFecha, reemplazando en '...' por 'd' para obtener el día, 'm' para los meses, 'yyyy' para los años, etc.

DateDiff(...,CampoFechaMenor,FechaReporte) nos devuelve un entero que representa la diferencia entre las fechas indicadas como parámetro de acuerdo a lo indicado en el primer parámetro, es decir, si usamos 'd' nos devolverá la diferencia de días entre ambas fechas, 'm' devolverá la diferencia de meses, etc.

El problema aquí es que si trabajamos a nivel de meses o años, el valor que retornará DateDiff() será la resta exacta de los valores de esos campos en las fechas, por ejemplo:




Fecha de Nacimiento: 29/08/1982
Fecha de Reporte : 01/01/2006
Resultado de DateDiff a nivel de años: 24


Pero si notamos realmente, la persona no tiene aún 24 años, tiene 23, la función ha devuelto (2006 - 1982)=24.

Si trabajamos a nivel de meses encontraremos un problema similar
Fecha de Nacimiento: 29/08/1982
Fecha de Reporte : 01/01/2006
Resultado de DateDiff a nivel de meses: 281

Si tomamos los 281 meses y los dividimos entre 12 para obtener la edad en años tendremos: 23.41, si piensas truncar el número a 23 observa el siguiente caso:
Fecha de Nacimiento: 29/08/1982
Fecha de Reporte : 01/08/2006
Resultado de DateDiif a nivel de meses: 288

Si tomamos los 288 y los dividimos entre 12 tendremos 24, pero la persona aún no tiene 24 años, por lo que no se podría truncar siempre al nivel de enteros el número obtenido.

Entonces usaremos la idea lógica, si a la persona del último caso se le preguntara cuántos años tiene, respondería 23 y en 28 días más tendría 24, entonces tendremos que bajar al nivel de días cuando sea necesario, es decir:

Edad = (Diferencia de meses / 12) y de ser el caso que el mes de nacimiento es el mismo mes sobre el cual se quiere obtener la edad, verificar los días, restando en uno la edad si aún no se ha llegado en el mes al día de su cumpleaños.

Traduciendo la idea a SQL con funciones de MS Access (y utilizando la tabla del ejemplo):

SELECT
P.NOMBRE, P.FECNAC,
DATEDIFF('m', P.FECNAC, NOW)/12
+
IIF(DATEPART('m',P.FECNAC)=DATEPART('m',NOW),
IIF(DATEPART('d',P.FECNAC)>DATEPART('d',NOW),-1,0)
, 0) AS EDAD FROM PERSONA P
WHERE P.FECNAC
Donde NOW es una función de MS Access que devuelve la fecha actual (se cambiaría por la fecha de generación del reporte o fecha de corte), IIF( , , ) es la selección condicional IIF(Evaluación,acción si evaluación es verdadera, acción si evaluación es falsa).

Notar que es importante la sección WHERE para asegurarnos que la persona exista antes de la fecha de corte y no obtener valores negativos.

Monday, August 21, 2006

[ASP.NET] Utilizando AJAX


Al realizar una aplicación web utilizando script de servidor debes haber notado que por cada envío de información al servidor, y por tanto intercambio de datos con el cliente, la pagina es recargada en su totalidad, es decir, el flujo de toda la pagina compilada es enviada al cliente, lo que implica tiempo en el viaje de datos por la red, el tiempo de respuesta para el usuario aumenta, por lo tanto la aplicación es menos eficiente que una aplicación de escritorio.

Puedes ver muchos de estos casos en la web, tan solo busca páginas que contengan formularios de registro, en muchos casos te solicitarán tu ubicación, quizás a través de controles de lista desplegable (combo box), si eliges digamos: País Perú, entonces se cargará otro combo box con los departamentos de dicho país, si para llenar dicho combo requiere ir al servidor, entonces verás la página recargarse.


AJAX nace como la tecnología que permite optimizar operaciones como esta, realizando llamadas asíncronas al servidor, solicitando segmentos específicos de la página, por ejemplo, que realice (continuando con el caso anterior de la ubicación) únicamente el envío de información de los departamentos dado que el país seleccionado es Perú, ésto implica transferir solamente una parte muy pequeña por la red en lugar de transmitir toda la página.

AJAX permite entonces lograr una operación mas eficiente de una página web, pero para lograrlo debes combinar el uso de script de cliente con script de servidor, veamos un ejemplo de como lograr esta tarea en ASP.NET 2003:

- Primero debes añadir la librería de AJAX a tu proyecto (Ajax.dll)
- Luego debes registrar a AJAX como un receptor de llamadas a servidor, para ello debes añadir las siguientes líneas en el archivo web.config de tu proyecto:



- Con ello la aplicación podrá procesar llamadas a través de AJAX, pero para que una pagina utilice esta propiedad debe indicarlo explícitamente, indicando estas líneas en el evento PageLoad en el codebehind de nuestra página:



De esta forma ya tenemos la página lista para utilizar AJAX, pero ahora, como lo utilizamos, vamos a ello:

- Para que una función sea tratada por la librería de AJAX debe indicarse (al igual que el caso anterior) explícitamente ésto, añadiendo antes de su declaración las siguientes líneas:

'Registramos el método como compatible con AJAX
_
Public Function f_MiFuncion() As MiValorDeRetorno
...

Ahora, lo grandioso de esto es que nos permite llamar a la función tanto de lado de servidor como de cliente. Para invocar a la función de lado de cliente podríamos utilizar un script como el siguiente:

function MiFuncionJava(...){
var valValida = NombrePaginaAspx.f_MiFuncion();
if (valValida.error != null){
alert("ocurrió un error");
return;
}else { ... }
}

Nota que antes del nombre de la función debes poner el nombre de la página, digamos que la página lleva por nombre frmRegistro, entonces el llamado sería frmRegistro.f_MiFuncion().

AJAX no permite solamente eso, también puede interactuar con estructuras definidas en nuestra arquitectura de servidor, por ejemplo, instanciar un objeto de una clase que tengamos en nuestro proyecto, para ello existen tags que debes ubicar antes de la declaración de la clase para indicar que pueda ser utilizada por AJAX, de forma similar que con el método visto anteriormente.

Como podrás ver, AJAX es una tecnología muy interesante y rica en funcionalidad. Si usas .NET 2005 puedes utilizar ATLAS, que no es mas sino la versión particularizada de AJAX para este framework, y al ser particular para .NET 2005 facilita la integración con tu proyecto, lo cual es un punto a favor para ATLAS. Enhorabuena.

[VB6] Trabajar con cadenas (español)

Cuando se trabaja con cadenas es usual encontrar segmentos como este:

VB6:
Dim str As String
str = "Select * from "
str = str & " Table1 where"
str = str & " Table1.Id=" & MyId

Bueno, en este caso, cada operación (la unión de cadenas) realiza por lo menos dos tareas antes de regresar la nueva cadena unida:
- primero un espacio en la ram es reservada para la nueva cadena (del tamaño de la cadena 1 mas la cadena 2)
- luego, la unión de cadenas es realizada (cada caracter de cada cadena es copiado al nuevo espacio en la ram)
y finalmente, el puntero a esa nueva posición de memoria es retornado.

En java puedes utilizar la clase StringBuffer, esta clase realiza las tareas con cadenas de forma más eficiente que el ejemplo anterior, en el framework de .NET puedes utilizar la clase StringBuilder de forma equivalente.

Pero que ocurre en VB6, ésto es lo que puedes hacer:
'Inicio de la clase
Private grupo() As String
Private lngElementos As Long

Public Sub Agregar(ByVal str As String)
ReDim Preserve grupo(lngItems) As String
grupo(lngElementos) = str
lngElementos = lngElementos + 1
End Sub

Public Function toString() As String
If lngElementos > 0 Then toString = Join(grupo, "")
End Function
'Fin de la clase

Join es una función del API de VB6 que realiza la unión de posiciones de memoria en una sola posición de forma realmente rápida, de forma que puedes ahorrar algo de tiempo en tus operaciones.

También he escuchado algunos consejos, como que es más eficiente comparar:

If MiCadena = vbNullString Then ...

que comparar:

If MiCadena = "" Then ...

Si realizas comparaciones como esas dentro de un bucle, entonces seguir este consejo puede ayudarte. Enhorabuena.

Sunday, August 20, 2006

[VB6] Working with strings

When you work with strings it's ussual to see something like this:

VB6:
Dim str As String
str = "Select * from "
str = str & " Table1 where"
str = str & " Table1.Id=" & MyId

well, in this case, each operation (the join of strings) performs at least 2 tasks before returning the new string:
- first a memory space in ram is reserved for the new string (size1 + size2)
- then the join of the strings is performed (each character of each string is copied to the new space in ram)
and finally the pointer to that memory position is returned to the final string.

In Java you can use the StringBuffer class, this object performs this tasks faster than the previous example, in .NET Framework you can use the StringBuilder class to perform the same tasks.

But what about VB6, here's what you can do:
'Start of class definition
Private group() As String
Private lngItems As Long

Public Sub Add(ByVal str As String)
ReDim Preserve group(lngItems) As String
group(lngItems) = str
lngItems = lngItems + 1
End Sub

Public Function toString() As String
If lngItems > 0 Then toString = Join(group, "")
End Function
'End of class definition

Join is an API function in VB6 that join slots of memory into one single slot, and it's performed really fast, so, this can help you if you want to cut some time on your operations.

I've also heard some tips, like it's faster to compare:

If MyString = vbNullString Then ...

Than

If MyString = "" Then ...

If you do this often in a one task job, then following this tips may help you. Have fun!.

[General] Parsing a grammar (parse)

Validating a string with the appropiate format can be difficult job, just imagine this scenario:

String s1 = "1+2+4+A"
The idea is to go through each character in the string and look for an invalid character, in this case 'A'. So far this is an easy thing to do, lets see something harder:

String s2 = "1+sum(1,2,3,sum(4,5,6,average(4,4,4,6)))"
In this case the string represents an arithmetic function, it contains calls to two different functions, sum and average, each case with different number of parameters. Here we have to look for appropiate usage of '(' ')' characters, ',' commas, etc.

As you can see, it can get harder if you need to use a richer grammar, lets get into the idea for performing this task:

Lets say you need a grammar for arithmetic operations (just '+' and '-'), something like this:

Definition of expression:
expression can be an expression '+' a factor
expression can be an expression '-' a factor
expression can be a factor

Definition of factor:
factor can be a NUMeric value
factor can be an expression

A program (code) that performs the verification of a stream (group of strings) it's called a parser, there are tools that generate this parser's, the hard work there is to define a grammar that support what we want to do, in linux you can use 'yacc' to generate the code for a parser in C/C++, you just have to write a file with the grammar, something like this:


The grammar above support expressions like: 4+(4+(8*2)*5).

But that's just jacc, there's also a tool that generates java code, it's called javacc.

You can use a parser on many tasks, arithmetic operations, language definition, compiler verification, etc. Write me if you want some examples on this subject, have fun.