Cargando un archivo en nuestro editor.

0 comentarios

Hasta ahora nuestro editor de textos era lo más tonto que existía, solo tenia un entorno y lo único que se podía hacer con el era cerrarlo, vamos a mejorar un poco esto y vamos a hacer que cargue fichero, para eso en la linea que habíamos creado como menú "cargar" vamos a modificarlo y le vamos a pedir que nos cree una acción.

JMenuItem cargar = new JMenuItem(new OpenFileAction(areaTexto));

como se puede comprobar con esto estamos pasandole en lugar de un String, con lo que queremos que ponga dentro, le pasamos una acción nueva con lo que queremos que se ejecute cuando le hagamos click. A la acción ademas le tenemos que indicar que parte de nuestro frame sera el afectado, de manera que como queremos que aquello que se cargue nos lo refleje en el área de edición pues le pasamos "areaTexto" que es el que le hace referencia.

En el caso de las acciones tenemos que hacer algo especial, por las costumbres que tenemos a la hora de programar en objetos que es que cuando se hacen acciones solemos dividir las que son, digamos... habituales que serán las "BaseAction" y luego tenemos las acciones especificas de lo que queremos realizar y en algunos casos extender alguna acción contenida en la "BaseAction". Nuestro "BaseAction" quedaría de la siguiente manera.

/*
 * Fichero: BaseAction.java
 * Autor: Liem Dazkun
 * Fecha: 17/10/11
 */
package LFR.Liem.Dazkun.Test.Java.Editor.Actions;

import java.awt.event.ActionEvent;

import java.io.File;
import java.io.FileNotFoundException;

import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.text.JTextComponent;


/**
 * Clase padre de las acciones de salvar fichero y cargar fichero con el código común
 * para ambas acciones.
.
 *
 * @author Chuidiang
 *
  */
public abstract class BaseAction extends AbstractAction
{
    /** 
     * Componente con el texto que se quiere salvar o en el que se quiere mostrar el
     * contenido del fichero.
     */
    protected JTextComponent componenteTexto;

    /** 
     * Panel que permite elegir un fichero del disco y navegar.  
     */
    private JFileChooser fileChooser = null;

    /** 
     * Si la acción va a ser para salvar o para cargar. 
     */
    private Opciones opcion;

    /** Enumerado con las posibles opciones para la acción */
    public enum Opciones
    {
     SALVAR, CARGAR;
    }

    /**
     * Crea un nuevo objeto AbstractAccionFichero.
     *
     * @param componenteTexto Componente de texto sobre el que actuar, salvando su
     * contenido o mostrando en él el contenido de un fichero.
     * @param opcion SALVAR o CARGAR.
     */
    public BaseAction(JTextComponent componenteTexto, Opciones opcion){
        this.componenteTexto = componenteTexto;
        this.opcion = opcion;
    }

    /**
     * Se ha pulsado el botón y se muestra el File Chooser.
     *
     * @param arg0 Evento de pulsación del botón.
     */
    public void actionPerformed(ActionEvent arg0)
    {
     // Se crea el FileChooser si no estaba creado.
        if (fileChooser == null)
        {
            fileChooser = new JFileChooser();
        }

        int opcionSeleccionada;

        // Se muestra el FileChooser como dialogo de salvar o de cargar según la opción
        // SALVAR o CARGAR que se haya pasado en el constructor.
        if (opcion == Opciones.SALVAR)
        {
            opcionSeleccionada = fileChooser.showSaveDialog(componenteTexto);
        }
        else
        {
            opcionSeleccionada = fileChooser.showOpenDialog(componenteTexto);
        }

        // Si el usuario elige un fichero y pulsa "OK"...
        if (JFileChooser.APPROVE_OPTION == opcionSeleccionada)
        {
         // Se obtiene el fichero
            File fichero = fileChooser.getSelectedFile();

            try
            {
             // Se salva o carga. Las clases hijas deben redefinir este método.
                actuarSobreElFichero(fichero);
            }
            catch (Exception e)
            {
             // Mensaje de error si se produce.
                JOptionPane.showMessageDialog(
                    componenteTexto, e, "Error en el fichero " + fichero,
                    JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * Las clases hijas deben salvar el contenido del area de texto en el fichero que se
     * pasa como parámetro o leer el fichero y meter su contenido en el area de texto.
     *
     * @param fichero Fichero que se debe leer o en el que se debe grabar.
     *
     * @throws FileNotFoundException Excepción si el fichero no existe.
     */
    protected abstract void actuarSobreElFichero(File fichero)
        throws FileNotFoundException;
}



La acción para cargar el fichero quedaría de la siguiente manera.


/*
 * Fichero: OpenFileAction.java
 * Autor: Liem Dazkun
 * Fecha: 17/10/11
 */
package LFR.Liem.Dazkun.Test.Java.Editor.Actions;

import java.awt.Event;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import javax.swing.Action;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;


/**
 * @author Liem Dazkun
 *
  */
public class OpenFileAction extends BaseAction
{
    /**
     * serial uid
     */
    private static final long serialVersionUID = 2L;

    /**
     * Crea un nuevo objeto OpenFileAction.
     *
     * @param componenteTexto Componente en el que mostrar el texto leido.
     */
    public OpenFileAction(JTextComponent componenteTexto){
     // Configurar clase padre para que sea CARGAR fichero.
        super(componenteTexto, Opciones.CARGAR);
        
        // Etiqueta y tecla aceleradora
        this.putValue(Action.NAME, "Abrir ...");
        this.putValue(Action.ACCELERATOR_KEY,KeyStroke.getAWTKeyStroke('O', Event.CTRL_MASK));
    }

    /**
     * Le el fichero que se le pasa y pone el contenido en el JTextArea.
     *
     * @param fichero Fichero a leer
     *
     * @throws FileNotFoundException No se puede abrir el fichero.
     */
    @Override
    protected void actuarSobreElFichero(File fichero)throws FileNotFoundException{
     // Se prepara el reader para leer el fichero. Un StringBuffer para compener el
     // texto total de forma eficiente, que diría rfilgueiras.
        BufferedReader reader = new BufferedReader(new FileReader(fichero));
        StringBuffer bufferTexto = new StringBuffer();

        try
        {
            String linea = reader.readLine();

            while (linea != null)
            {
             // Se va añadiendo las líneas que se leen y un separador de línea
             // adecuado para el sistema operativo. Este último puede ser /n o /r/n
             // dependiendo de si es unix o windows. System.getProperty("line.separator")
             // nos da el adecuado.
                bufferTexto.append(linea);
                bufferTexto.append(System.getProperty("line.separator"));
                
                // Siguiente linea.
                linea = reader.readLine();
            }
        }
        catch (IOException e)
        {
            JOptionPane.showMessageDialog(
                componenteTexto, e, "Error al leer fichero",
                JOptionPane.ERROR_MESSAGE);
        }

        // Se pone el texto leido en el JTextArea.
        componenteTexto.setText(bufferTexto.toString());
    }
}



Con estas dos acciones escritas podríamos ejecutar la carga de un fichero, de momento no se podría realizar nada más, pero esto seria un claro ejemplo de lo que podemos hacer con este fichero.

serialVersionUID ¿Para que sirve?

0 comentarios

Hoy me he encontrado con que no recordaba exactamente para que servia el parámetro serialVersionUID, afortunadamente no estamos en los 80 que o lo recordabas o estabas perdido. 

private static final long serialVersionUID = 1L;

Básicamente esta variable sirve para decirle a nuestro proyecto en que versión se encuentra en estos momentos y esto se debe a que todo objeto que sea susceptible a implementar un "Serializable" y se ejecute desde un entorno como Swing o Applet puede sufrir modificaciones. Pongamos que hemos creado un Applet que nos ayuda a cargar y visualizar un fichero, el programa serializara una serie de objetos al cerrarse y los lee en la siguiente ejecución para mantener los valores que tenia antes. Cuando evoluciones tu programa si no modificas el serialVersionUID e intentas leer los objetos que fueron serializados te los devolverá como erróneos, eso pasa por que los objetos locales no saben que el programa a sido modificado.

De todas maneras esta información esta extraída de: http://www.lawebdelprogramador.com creo que no necesita más presentación y de http://chuwiki.chuidiang.org que es una web que no conocía pero que esta muy bien, es más, las practicas que estoy haciendo proceden de su WIKI, la cual aconsejo leer. Se que mi descripción no es muy clara pero en todo caso en ambas paginas web se pueden encontrar sendos resultados que pueden facilita que se entiendan estas pequeñas cosas.

Fe de erratas:
Por lo visto, tal y como lo describo parece que el serialVersionUID tenga que ser el mismo que la versión del programa completo, pero no es así, el serialVersionUID  hace referencia al .class generado a partir del Java de manera que podemos tener más de un serialVersionUID en todo el programa dependiendo de que se haya modificado.

semana del año en Java

0 comentarios

En esta ocasión estamos creando el algoritmo definido por la ISO 8601 para poder poner el día de la semana en la que nos encontramos.

public class SemanaAnno{
	public Integer semanaAnno(Calendar fecha){
		//conseguimos el dia de año, un año tiene 365 dias... 366 si es bisiesto.
		Calendar cal = new GregorianCalendar();
		int mes = cal.get(fecha.MONTH)+1;
		int a = mes==1||mes==2?cal.get(fecha.YEAR)-1:cal.get(fecha.YEAR);
		int b = (a/4)-(a/100)+(a/400);
		int c = ((a-1)/4)-((a-1)/100)+((a-1)/400);
		int s = b-c;
		int e = mes==1||mes==2?0:s+1;
		int f;
		if(mes==1||mes==2){
			f=cal.get(fecha.DAY_OF_MONTH)-1+(31*(mes-1));
		}else{
			f=cal.get(fecha.DAY_OF_MONTH)+((153*(mes-3)+2)/5)+58+s;
		}
		int g = (a+b)%7;
		int d = (f+g-e)%7;
		int n = f+3-d;
		if(n<0){//Corresponde al año anterior
			return (53-((g-s)/5));
		}else if(n>364+s){//Corresponde al año siguiente
			return 1;//corresponde al año siguiente
		}else{//corresponde al año en curso.
			return (n/7)+1;
		}
	}
}

Como seguro que os lo vais a preguntar ya lo cuento de antemano, las variables no son nemotécnica por que realmente no se muy bien que es lo que estamos haciendo en cada paso, lo cual supone un gran fallo para cualquier programador, pero la verdad es que este "trabajito" ya me estaba trayendo por la calle de la amargura, no he conseguido encontrar el ISO 8601 en toda su extensión, solamente pequeñas pretensiones que indicaban el tipo de formateo permitido por la ISO, la verdad es que eso me sirvió de poco.

Las paginas que encontré con el calculo eran solamente aproximaciones que yo ya había probado, desde contar los días hasta contar el día total en el que estamos en el año, vamos lo que viene a ser el día Juliano, sumarle el día en que comenzó la semana, cosa que ya hemos calculado antes, vamos que funcionar funcionaba, pero que pasaba cuando decías de contar el 1 de Enero de cualquier año, pocas veces coincide que la semana del 1 comience en lunes, por lo que las cuentas solo salían para ciertos meses y para ejemplo un botón, el 1 de Enero del 2012 es el domingo de la semana 52 del año 2011, según nuestro recuento que pasaría,  como ha comenzado el año en domingo le sumamos cinco, se suman cinco por que la semana la iniciamos en lunes, 6/7 el mod de esta división nos daría un total de 0, se puede realizar una división normal ya que estamos dividiendo enteros y asignándoselo a enteros. Muy bien y vosotros diréis, pues ya esta si es 0 se corresponde a la ultima del año anterior, pero esto ya seria regresivo, tendríamos que volver a hacer todo pero con el 31 de Diciembre del año anterior. Pero que paso en Enero del 2009, según las cuentas hasta el día 5 no podemos marcar la primera semana del año, esto se trataría de otra excepción, por que si buscamos los calendarios de esos años nos encontraremos que la primera semana del año fue la misma que contenía el día uno.

Con el código que aquí comparto todos estos datos quedan cubiertos dando en definitiva el día exacto, igual me hacen falta más pruebas comparando con los calendarios de otros años, pero en un principio y por las pruebas realizadas este código es correcto.

El código original del que me he basado es un JavaScript que encontré en la red, lamento no poder dar referencias pero es que no me acuerdo, me lo copie en el NotePad++ y se me olvido guardar la pagina en favoritos, realmente deseo que les guste el aporte y les pueda resultar útil.