delphsim.model.Epidemia.java Source code

Java tutorial

Introduction

Here is the source code for delphsim.model.Epidemia.java

Source

/** 
 * Copyright 2008 Vctor Enrique Tamames,
 * Universidad de Valladolid, Espaa.
 * 
 * This file is part of DelphSim.
 *
 * DelphSim is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * DelphSim is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * DelphSim. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * DelphSim (Delphos Simulator), simulador de epidemias desarrollado como
 * Proyecto Fin de Carrera de Ingeniera Informtica para la Escuela Tcnica
 * Superior de Ingeniera Informtica de la Universidad de Valladolid.
 */
package delphsim.model;

import delphsim.util.*;
import delphsim.util.random.*;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Vector;
import java.util.HashSet;

import javax.swing.DefaultListModel;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.tree.DefaultElement;

import org.nfunk.jep.FunctionTable;
import org.nfunk.jep.JEP;
import org.nfunk.jep.function.*;

import org.xml.sax.SAXException;

/**
 * Clase representa la epidemia que se est modelando y se va a simular.
 * Contiene referencias a todos los elementos que la componen: parmetros,
 * procesos, poblacin, compartimentos generados, palabras reservadas, etc.
 * Tambin se encarga de cargar/guardar su informacin desde/a archivos XML.
 * @author Vctor E. Tamames Gmez
 */
public class Epidemia implements Cloneable {

    /**
     * Tiempo mximo de simulacin.
     */
    private float tiempoSimulacion = 0;

    /**
     * Unidad de tiempo en la que se ha definido el modelo.
     */
    private String unidadTiempo;

    /**
     * Conjunto de parmetros definidos para este modelo.
     */
    private Parametro[] parametros = new Parametro[0];

    /**
     * Conjunto de procesos definidos para este modelo.
     */
    private Proceso[] procesos = new Proceso[0];

    /**
     * Poblacin modelada para esta epidemia.
     */
    private Poblacion poblacion;

    /**
     * Conjunto de compartimentos entre los que se distribuye la poblacin.
     */
    private Compartimento[] compartimentos = new Compartimento[0];

    /**
     * Conjunto de palabras clave o 'atajos' que permiten al usuario hacer
     * referencia a varios compartimentos al mismo tiempo.
     */
    private Atajo[] atajos = new Atajo[0];

    /**
     * Conjunto de resultados a obtener al trmino de una simulacin.
     */
    private Resultado[] resultados = new Resultado[0];

    /**
     * Palabras reservadas: contiene los nombres de los parmetros, de los
     * procesos y de las categoras, los cuales no pueden repetirse.
     */
    private HashSet palabrasReservadas = new HashSet();

    /**
     * Los nombres de los atajos para poder crearlos.
     */
    private Vector palabrasAtajos = new Vector();

    /**
     * Constructor de la clase. Crea un nuevo objeto y reserva por defecto las
     * funciones estndar disponibles.
     */
    public Epidemia() {
        // Crear un nuevo JEP de DelphSim y reservar palabras
        JEP jep = Epidemia.CrearDelphSimJEP();
        FunctionTable ft = jep.getFunctionTable();
        for (Object e : ft.keySet()) {
            this.palabrasReservadas.add(e.toString());
        }
    }

    /**
     * Mtodo que permite cambiar el tiempo de simulacin de la epidemia.
     * @param tiempoSimulacionEpi El nuevo tiempo de simulacin.
     */
    public void setTiempoSimulacion(float tiempoSimulacionEpi) {
        this.tiempoSimulacion = tiempoSimulacionEpi;
    }

    /**
     * Mtodo que permite cambiar el tiempo de simulacin, transformndolo a las
     * unidades indicadas como parmetro.
     * @param tiempoSimulacionEpi Cunto tiempo debe simularse la epidemia.
     * @param unidadTiempo La unidad de tiempo en que se da el otro parmetro.
     *                     Es un nmero de 0 a 7 para Segundos, Minutos, Horas,
     *                     Das, Semanas, Meses o Aos en ese orden.
     */
    public void setTiempoSimulacion(float tiempoSimulacionEpi, int unidadTiempo) {
        if (this.unidadTiempo.equals("Segundos")) {
            switch (unidadTiempo) {
            case 0:
                this.tiempoSimulacion = tiempoSimulacionEpi;
                break;
            case 1:
                this.tiempoSimulacion = tiempoSimulacionEpi * 60;
                break;
            case 2:
                this.tiempoSimulacion = tiempoSimulacionEpi * 3600;
                break;
            case 3:
                this.tiempoSimulacion = tiempoSimulacionEpi * 86400;
                break;
            case 4:
                this.tiempoSimulacion = tiempoSimulacionEpi * 604800;
                break;
            case 5:
                this.tiempoSimulacion = tiempoSimulacionEpi * 2592000;
                break;
            case 6:
                this.tiempoSimulacion = tiempoSimulacionEpi * 31536000;
                break;
            default:
                return;
            }
        } else if (this.unidadTiempo.equals("Minutos")) {
            switch (unidadTiempo) {
            case 0:
                this.tiempoSimulacion = tiempoSimulacionEpi / 60;
                break;
            case 1:
                this.tiempoSimulacion = tiempoSimulacionEpi;
                break;
            case 2:
                this.tiempoSimulacion = tiempoSimulacionEpi * 60;
                break;
            case 3:
                this.tiempoSimulacion = tiempoSimulacionEpi * 1440;
                break;
            case 4:
                this.tiempoSimulacion = tiempoSimulacionEpi * 10080;
                break;
            case 5:
                this.tiempoSimulacion = tiempoSimulacionEpi * 43200;
                break;
            case 6:
                this.tiempoSimulacion = tiempoSimulacionEpi * 525600;
                break;
            default:
                return;
            }
        } else if (this.unidadTiempo.equals("Horas")) {
            switch (unidadTiempo) {
            case 0:
                this.tiempoSimulacion = tiempoSimulacionEpi / 360;
                break;
            case 1:
                this.tiempoSimulacion = tiempoSimulacionEpi / 60;
                break;
            case 2:
                this.tiempoSimulacion = tiempoSimulacionEpi;
                break;
            case 3:
                this.tiempoSimulacion = tiempoSimulacionEpi * 24;
                break;
            case 4:
                this.tiempoSimulacion = tiempoSimulacionEpi * 168;
                break;
            case 5:
                this.tiempoSimulacion = tiempoSimulacionEpi * 720;
                break;
            case 6:
                this.tiempoSimulacion = tiempoSimulacionEpi * 8760;
                break;
            default:
                return;
            }
        } else if (this.unidadTiempo.equals("Das")) {
            switch (unidadTiempo) {
            case 0:
                this.tiempoSimulacion = tiempoSimulacionEpi / 86400;
                break;
            case 1:
                this.tiempoSimulacion = tiempoSimulacionEpi / 1440;
                break;
            case 2:
                this.tiempoSimulacion = tiempoSimulacionEpi / 24;
                break;
            case 3:
                this.tiempoSimulacion = tiempoSimulacionEpi;
                break;
            case 4:
                this.tiempoSimulacion = tiempoSimulacionEpi * 7;
                break;
            case 5:
                this.tiempoSimulacion = tiempoSimulacionEpi * 30;
                break;
            case 6:
                this.tiempoSimulacion = tiempoSimulacionEpi * 365;
                break;
            default:
                return;
            }
        } else if (this.unidadTiempo.equals("Semanas")) {
            switch (unidadTiempo) {
            case 0:
                this.tiempoSimulacion = tiempoSimulacionEpi / 604800;
                break;
            case 1:
                this.tiempoSimulacion = tiempoSimulacionEpi / 10080;
                break;
            case 2:
                this.tiempoSimulacion = tiempoSimulacionEpi / 168;
                break;
            case 3:
                this.tiempoSimulacion = tiempoSimulacionEpi / 7;
                break;
            case 4:
                this.tiempoSimulacion = tiempoSimulacionEpi;
                break;
            case 5:
                this.tiempoSimulacion = (tiempoSimulacionEpi / 7) * 30;
                break;
            case 6:
                this.tiempoSimulacion = (tiempoSimulacionEpi / 7) * 365;
                break;
            default:
                return;
            }
        } else if (this.unidadTiempo.equals("Meses")) {
            switch (unidadTiempo) {
            case 0:
                this.tiempoSimulacion = tiempoSimulacionEpi / 2592000;
                break;
            case 1:
                this.tiempoSimulacion = tiempoSimulacionEpi / 43200;
                break;
            case 2:
                this.tiempoSimulacion = tiempoSimulacionEpi / 720;
                break;
            case 3:
                this.tiempoSimulacion = tiempoSimulacionEpi / 30;
                break;
            case 4:
                this.tiempoSimulacion = (tiempoSimulacionEpi / 30) * 7;
                break;
            case 5:
                this.tiempoSimulacion = tiempoSimulacionEpi;
                break;
            case 6:
                this.tiempoSimulacion = (tiempoSimulacionEpi / 30) * 365;
                break;
            default:
                return;
            }
        } else if (this.unidadTiempo.equals("Aos")) {
            switch (unidadTiempo) {
            case 0:
                this.tiempoSimulacion = tiempoSimulacionEpi / 31536000;
                break;
            case 1:
                this.tiempoSimulacion = tiempoSimulacionEpi / 525600;
                break;
            case 2:
                this.tiempoSimulacion = tiempoSimulacionEpi / 8760;
                break;
            case 3:
                this.tiempoSimulacion = tiempoSimulacionEpi / 365;
                break;
            case 4:
                this.tiempoSimulacion = (tiempoSimulacionEpi / 365) * 7;
                break;
            case 5:
                this.tiempoSimulacion = (tiempoSimulacionEpi / 365) * 30;
                break;
            case 6:
                this.tiempoSimulacion = tiempoSimulacionEpi;
                break;
            default:
                return;
            }
        }
    }

    /**
     * Mtodo que permite obtener el tiempo de simulacin de la epidemia.
     * @return El tiempo total de simulacin.
     */
    public float getTiempoSimulacion() {
        return this.tiempoSimulacion;
    }

    /**
     * Mtodo que permite cambiar la unidad de tiempo en que estn especificados
     * los elementos del modelo.
     * @param unidadTiempoEpi La nueva unidad de tiempo.
     */
    public void setUnidadTiempo(String unidadTiempoEpi) {
        this.unidadTiempo = unidadTiempoEpi;
    }

    /**
     * Mtodo que permite obtener la unidad de tiempo en que estn especificados
     * los elementos del modelo.
     * @return La unidad de tiempo en una cadena de texto.
     */
    public String getUnidadTiempo() {
        return this.unidadTiempo;
    }

    /**
     * Mtodo para cambiar la poblacin de la epidemia.
     * @param poblacionEpi La nueva poblacin.
     */
    public void setPoblacion(Poblacion poblacionEpi) {
        this.poblacion = poblacionEpi;
    }

    /**
     * Mtodo para obtener la poblacin de la epidemia.
     * @return La poblacin de la epidemia.
     */
    public Poblacion getPoblacion() {
        return this.poblacion;
    }

    /**
     * Mtodo para cambiar los compartimentos de la poblacin.
     * @param compartimentosEpi El nuevo conjunto de compartimentos.
     */
    public void setCompartimentos(Compartimento[] compartimentosEpi) {
        this.compartimentos = compartimentosEpi;
    }

    /**
     * Mtodo para cambiar un compartimento de la poblacin.
     * @param compartimentosEpi El nuevo compartimento.
     * @param indice El ndice del compartimento a sustituir por el nuevo.
     */
    public void setCompartimento(Compartimento compartimentosEpi, int indice) {
        this.compartimentos[indice] = compartimentosEpi;
    }

    /**
     * Mtodo para obtener el conjunto de compartimentos de la poblacin.
     * @return El conjunto de compartimentos de la poblacin.
     */
    public Compartimento[] getCompartimentos() {
        return this.compartimentos;
    }

    /**
     * Mtodo para obtener un compartimento.
     * @param indice El ndice del compartimento.
     * @return El compartimento.
     */
    public Compartimento getCompartimento(int indice) {
        return this.compartimentos[indice];
    }

    /**
     * Mtodo para obtener un compartimento.
     * @param nombre El nombre del compartimento.
     * @return El compartimento.
     */
    public Compartimento getCompartimento(String nombre) {
        for (int i = 0; i < this.compartimentos.length; i++) {
            if (this.compartimentos[i].getNombre().equals(nombre))
                return this.compartimentos[i];
        }
        return null;
    }

    /**
     * Mtodo para cambiar el conjunto de parmetros del modelo.
     * @param parametrosEpi El nuevo conjunto de parmetros.
     */
    public void setParametros(Parametro[] parametrosEpi) {
        this.parametros = parametrosEpi;
    }

    /**
     * Mtodo para cambiar un parmetro del modelo.
     * @param parametroEpi El nuevo parmetro.
     * @param indice El ndice del parmetro a reemplazar.
     */
    public void setParametro(Parametro parametroEpi, int indice) {
        this.parametros[indice] = parametroEpi;
    }

    /**
     * Mtodo para obtener el conjunto de parmetros del modelo.
     * @return El conjunto de parmetros del modelo.
     */
    public Parametro[] getParametros() {
        return this.parametros;
    }

    /**
     * Mtodo para obtener un parmetro del modelo.
     * @param indice El ndice del parmetro a obtener.
     * @return El parmetro.
     */
    public Parametro getParametro(int indice) {
        return this.parametros[indice];
    }

    /**
     * Mtodo para obtener un parmetro del modelo.
     * @param nombre El nombre del parmetro a obtener.
     * @return El parmetro.
     */
    public Parametro getParametro(String nombre) {
        for (int i = 0; i < this.parametros.length; i++) {
            if (this.parametros[i].getNombre().equals(nombre))
                return this.parametros[i];
        }
        return null;
    }

    /**
     * Aade un parmetro nuevo a la lista actual de parmetros.
     * @param param El parmetro a aadir.
     */
    public void anadirParametro(Parametro param) {
        Parametro[] nuevaLista = new Parametro[this.parametros.length + 1];
        for (int i = 0; i < this.parametros.length; i++) {
            nuevaLista[i] = this.parametros[i];
        }
        nuevaLista[this.parametros.length] = param;
        this.parametros = nuevaLista;
    }

    /**
     * Elimina el parmetro indicado por el ndice de la lista de parmetros.
     * @param indice La posicin en la que se encuentra el parmetro.
     */
    public void eliminarParametro(int indice) {
        // Rehacer la lista, pero sin el parmetro eliminado
        String nombreParEliminado = this.parametros[indice].getNombre();
        Parametro[] nuevosParametros = new Parametro[this.parametros.length - 1];
        for (int i = 0; i < this.parametros.length; i++) {
            if (i < indice) {
                nuevosParametros[i] = this.parametros[i];
            } else if (i > indice) {
                nuevosParametros[i - 1] = this.parametros[i];
            }
        }

        // Este parametro podra depender de otros, eliminar esas dependencias
        for (Parametro par : nuevosParametros) {
            par.eliminarParametroVinculado(nombreParEliminado);
        }

        // El nombre de este parmetro deja de estar reservado
        this.palabrasReservadas.remove(nombreParEliminado);

        // Asignar la nueva lista
        this.parametros = nuevosParametros;
    }

    /**
     * Intercambia el parmetro de la posicin posInicial con el de la posicin
     * posFinal.
     * @param posInicial Posicin origen del parmetro.
     * @param posFinal Posicin destino del parmetro.
     */
    public void moverParametro(int posInicial, int posFinal) {
        Parametro origen = this.getParametro(posInicial);
        Parametro destino = this.getParametro(posFinal);
        this.parametros[posInicial] = destino;
        this.parametros[posFinal] = origen;
    }

    /**
     * Mtodo para cambiar el conjunto de procesos del modelo.
     * @param procesosEpi El nuevo conjunto de procesos.
     */
    public void setProcesos(Proceso[] procesosEpi) {
        this.procesos = procesosEpi;
    }

    /**
     * Mtodo para cambiar un proceso del modelo.
     * @param procesoEpi El nuevo proceso.
     * @param indice El ndice del proceso a reemplazar.
     */
    public void setProceso(Proceso procesoEpi, int indice) {
        this.procesos[indice] = procesoEpi;
    }

    /**
     * Mtodo para obtener el conjunto de procesos del modelo.
     * @return El conjunto de procesos.
     */
    public Proceso[] getProcesos() {
        return this.procesos;
    }

    /**
     * Mtodo para obtener un proceso del modelo.
     * @param indice El ndice del proceso a obtener.
     * @return El proceso.
     */
    public Proceso getProceso(int indice) {
        return this.procesos[indice];
    }

    /**
     * Mtodo para obtener un proceso del modelo.
     * @param nombre El nombre del proceso a obtener.
     * @return El proceso.
     */
    public Proceso getProceso(String nombre) {
        for (int i = 0; i < this.procesos.length; i++) {
            if (this.procesos[i].getNombre().equals(nombre))
                return this.procesos[i];
        }
        return null;
    }

    /**
     * Aade un proceso nuevo a la lista actual de procesos.
     * @param proc El proceso a aadir.
     */
    public void anadirProceso(Proceso proc) {
        Proceso[] nuevaLista = new Proceso[this.procesos.length + 1];
        for (int i = 0; i < this.procesos.length; i++) {
            nuevaLista[i] = this.procesos[i];
        }
        nuevaLista[this.procesos.length] = proc;
        this.procesos = nuevaLista;
    }

    /**
     * Elimina el proceso indicado por el ndice de la lista de procesos.
     * @param indice La posicin en la que se encuentra el proceso.
     */
    public void eliminarProceso(int indice) {
        // Rehacer la lista, pero sin el proceso eliminado
        String nombreProcEliminado = this.procesos[indice].getNombre();
        Proceso[] nuevosProcesos = new Proceso[this.procesos.length - 1];
        for (int i = 0; i < this.procesos.length; i++) {
            if (i < indice) {
                nuevosProcesos[i] = this.procesos[i];
            } else if (i > indice) {
                nuevosProcesos[i - 1] = this.procesos[i];
            }
        }

        // Este proceso podra depender de otros parametros, procesos o
        // compartimentos; eliminar esas dependencias
        for (Parametro par : this.parametros) {
            par.eliminarProcesoVinculado(nombreProcEliminado);
        }
        for (Proceso proc : this.procesos) {
            proc.eliminarProcesoVinculado(nombreProcEliminado);
        }
        for (Compartimento comp : this.compartimentos) {
            comp.eliminarProcesoVinculado(nombreProcEliminado);
        }

        // El nombre de este proceso deja de estar reservado
        this.palabrasReservadas.remove(nombreProcEliminado);

        // Asignar la nueva lista
        this.procesos = nuevosProcesos;
    }

    /**
     * Intercambia el proceso de la posicin posInicial con el de la posicin
     * posFinal.
     * @param posInicial Posicin origen del proceso.
     * @param posFinal Posicin destino del proceso.
     */
    public void moverProceso(int posInicial, int posFinal) {
        Proceso origen = this.getProceso(posInicial);
        Proceso destino = this.getProceso(posFinal);
        this.procesos[posInicial] = destino;
        this.procesos[posFinal] = origen;
    }

    /**
     * Mtodo para cambiar el conjunto de atajos del modelo.
     * @param atajosEpi El nuevo conjunto de atajos.
     */
    public void setAtajos(Atajo[] atajosEpi) {
        this.atajos = atajosEpi;
    }

    /**
     * Mtodo para cambiar un atajo del modelo.
     * @param ataj El nuevo atajo.
     * @param indice El ndice del atajo a reemplazar.
     */
    public void setAtajo(Atajo ataj, int indice) {
        this.atajos[indice] = ataj;
    }

    /**
     * Mtodo para obtener el conjunto de atajos del modelo.
     * @return El conjunto de atajos.
     */
    public Atajo[] getAtajos() {
        return this.atajos;
    }

    /**
     * Mtodo para obtener un atajo del modelo.
     * @param indice El ndice del atajo a obtener.
     * @return El atajo.
     */
    public Atajo getAtajo(int indice) {
        return this.atajos[indice];
    }

    /**
     * Mtodo para obtener un atajo del modelo.
     * @param nombre El nombre del atajo a obtener.
     * @return El atajo.
     */
    public Atajo getAtajo(String nombre) {
        for (int i = 0; i < this.atajos.length; i++) {
            if (this.atajos[i].getNombre().equals(nombre))
                return this.atajos[i];
        }
        return null;
    }

    /**
     * Mtodo para cambiar el conjunto de resultados del modelo.
     * @param res El nuevo conjunto de resultados.
     */
    public void setResultados(Resultado[] res) {
        if (resultados != null) {
            for (int i = 0; i < this.resultados.length; i++) {
                this.resultados[i].eliminarFunciones();
            }
        }
        this.resultados = res;
    }

    /**
     * Mtodo para cambiar un resultado del modelo.
     * @param res El nuevo resultado.
     * @param indice El ndice del resultado a reemplazar.
     */
    public void setResultado(Resultado res, int indice) {
        if (this.resultados[indice] != null) {
            this.resultados[indice].eliminarFunciones();
        }
        this.resultados[indice] = res;
    }

    /**
     * Mtodo para obtener el conjunto de resultados del modelo.
     * @return El conjunto de resultados.
     */
    public Resultado[] getResultados() {
        return this.resultados;
    }

    /**
     * Mtodo para obtener un resultado del modelo.
     * @param indice El ndice del resultado a obtener.
     * @return El resultado.
     */
    public Resultado getResultado(int indice) {
        return this.resultados[indice];
    }

    /**
     * Aade un resultado nuevo a la lista actual de resultados.
     * @param res El resultado a aadir.
     */
    public void anadirResultado(Resultado res) {
        Resultado[] nuevaLista = new Resultado[this.resultados.length + 1];
        for (int i = 0; i < this.resultados.length; i++) {
            nuevaLista[i] = this.resultados[i];
        }
        nuevaLista[this.resultados.length] = res;
        this.resultados = nuevaLista;
    }

    /**
     * Elimina el resultado indicado por el ndice de la lista de resultados.
     * @param indice La posicin en la que se encuentra el resultado.
     */
    public void eliminarResultado(int indice) {
        // Rehacer la lista, pero sin el resultado eliminado
        Resultado[] nuevosResultados = new Resultado[this.resultados.length - 1];
        for (int i = 0; i < this.resultados.length; i++) {
            if (i < indice) {
                nuevosResultados[i] = this.resultados[i];
            } else if (i > indice) {
                nuevosResultados[i - 1] = this.resultados[i];
            }
        }
        // Asignar la nueva lista
        this.resultados = nuevosResultados;
    }

    /**
     * Vaca la lista de resultados.
     */
    public void eliminarResultados() {
        if (this.resultados != null) {
            for (int i = 0; i < this.resultados.length; i++) {
                this.resultados[i].eliminarFunciones();
            }
        }
        File f = new File(new File(System.getProperty("java.class.path")).getParent() + File.separator + "temp"
                + File.separator + "tiempo_.temp");
        if (f.exists()) {
            if (!f.delete()) {
                f.deleteOnExit();
            }
        }
        this.resultados = new Resultado[0];
    }

    /**
     * Mtodo para cambiar el conjunto de palabras reservadas.
     * @param pRes El nuevo conjunto de palabras reservadas.
     */
    public void setPalabrasReservadas(HashSet pRes) {
        this.palabrasReservadas = pRes;
    }

    /**
     * Mtodo para obtener el conjunto de palabras reservadas.
     * @return El conjunto de palabras reservadas.
     */
    public HashSet getPalabrasReservadas() {
        return this.palabrasReservadas;
    }

    /**
     * Mtodo para cambiar el conjunto de nombres de atajos.
     * @param pAjs El nuevo conjunto de nombres de atajos.
     */
    public void setPalabrasAtajos(Vector pAjs) {
        this.palabrasAtajos = pAjs;
    }

    /**
     * Mtodo para obtener el conjunto de nombres de atajos.
     * @return El conjunto de nombres de atajos.
     */
    public Vector getPalabrasAtajos() {
        return this.palabrasAtajos;
    }

    /**
     * Comprueba si el elemento indicado ya est definido, bien sea como
     * parmetro, bien como proceso, bien como compartimento, o como atajo.
     * @param elemento  El elemento a comprobar, de tipo Parametro, Proceso,
     *                  Compartimento o String (el nombre directamente).
     * @return Si ya est definido, el tipo del objeto existente. Si no, null.
     */
    public Class estaDefinido(Object elemento) {
        String nombre = "";
        if (elemento.getClass() == Parametro.class) {
            nombre = ((Parametro) elemento).getNombre();
        } else if (elemento.getClass() == Proceso.class) {
            nombre = ((Proceso) elemento).getNombre();
        } else if (elemento.getClass() == Compartimento.class) {
            nombre = ((Compartimento) elemento).getNombre();
        } else {
            nombre = elemento.toString();
        }
        for (int i = 0; i < this.parametros.length; i++) {
            if (this.parametros[i].getNombre().equals(nombre)) {
                return Parametro.class;
            }
        }
        for (int i = 0; i < this.procesos.length; i++) {
            if (this.procesos[i].getNombre().equals(nombre)) {
                return Proceso.class;
            }
        }
        for (int i = 0; i < this.compartimentos.length; i++) {
            if (this.compartimentos[i].getNombre().equals(nombre)) {
                return Compartimento.class;
            }
        }
        if (this.palabrasAtajos.contains(nombre)) {
            return Atajo.class;
        }
        JEP jep = Epidemia.CrearDelphSimJEP();
        FunctionTable functionTable = jep.getFunctionTable();
        if (functionTable.containsKey(elemento)) {
            return PostfixMathCommand.class;
        }
        return null;
    }

    /**
     * Analiza sintcticamente el archivo XML pasado como parmetro y devuelve
     * un objeto tipo rbol.
     * @param archivoOrigen El archivo XML a analizar.
     * @return Un objeto tipo rbol para poder trabajar con la informacin.
     * @throws org.dom4j.DocumentException Si hay algn problema al leer el archivo.
     */
    private Document analizar(File archivoOrigen) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(archivoOrigen);
        return document;
    }

    /**
     * Abre un archivo XML y, si es correcto y vlido, carga su informacin en
     * esta epidemia.
     * @param XMLSchema Ruta relativa al archivo .xsd contra el que hay que
     *                  validar el fichero que se quiere abrir.
     * @param archivoOrigen El fichero que se quiere abrir.
     * @return Una cadena con los errores detectados. Vaca si no hay ninguno.
     * @throws org.dom4j.DocumentException Si hay algn problema al analizar el archivo.
     * @throws org.xml.sax.SAXException Si hay algn problema al validar el archivo.
     * @throws java.io.IOException Si hay algn problema al leer el archivo.
     */
    public String abrirXML(String XMLSchema, File archivoOrigen)
            throws DocumentException, SAXException, IOException {
        // Comprobar que el archivo se ajusta al esquema XML de DelphSim
        SchemaValidator validadorEsquemas = new SchemaValidator();
        validadorEsquemas.setXMLSchemaURL(new File(System.getProperty("java.class.path")).getParent() + XMLSchema);
        String errores = validadorEsquemas.validar(archivoOrigen);
        if (errores.equals("")) {
            // Abrir el archivo XML de la ruta especificada y coger el elemento "epidemia" (raz)
            Document archivoXML = this.analizar(archivoOrigen);
            Element elementoEpidemia = archivoXML.getRootElement();
            if (elementoEpidemia.attributeValue("unidadTiempo") != null) {
                this.unidadTiempo = elementoEpidemia.attributeValue("unidadTiempo");
            }

            // Iterar para recuperar todos los elementos "parmetro" y crear los objetos
            Vector vectorParametros = new Vector();
            this.parametros = new Parametro[elementoEpidemia.elements("parametro").size()];
            int indice = 0;
            for (Iterator i = elementoEpidemia.elementIterator("parametro"); i.hasNext();) {
                Element elementoParametro = (Element) i.next();
                this.parametros[indice] = new Parametro();
                this.parametros[indice].cargarDesdeXML(elementoParametro);
                vectorParametros.add(this.parametros[indice].getNombre());
                this.palabrasReservadas.add(this.parametros[indice].getNombre());
                indice++;
            }

            // Iterar para recuperar todos los elementos "proceso" y crear los objetos
            Vector vectorProcesos = new Vector();
            this.procesos = new Proceso[elementoEpidemia.elements("proceso").size()];
            indice = 0;
            for (Iterator i = elementoEpidemia.elementIterator("proceso"); i.hasNext();) {
                Element elementoProceso = (Element) i.next();
                this.procesos[indice] = new Proceso();
                this.procesos[indice].cargarDesdeXML(elementoProceso);
                vectorProcesos.add(this.procesos[indice].getNombre());
                this.palabrasReservadas.add(this.procesos[indice].getNombre());
                indice++;
            }

            // Comprobar que, dentro de los parmetros, parametrosVinculados y
            // procesosVinculados usen nombres definidos y adems que
            // parametrosVinculados estn en el orden apropiado.
            for (int i = 0; i < this.parametros.length; i++) {
                String nombrePar = this.parametros[i].getNombre();
                String[] parVinc = this.parametros[i].getParametrosVinculados();
                String[] procVinc = this.parametros[i].getProcesosVinculados();
                for (int j = 0; j < parVinc.length; j++) {
                    int posicion = vectorParametros.indexOf(parVinc[j]);
                    if (posicion == -1) {
                        errores += String.format(
                                "<Error> El parmetro '%s' se utiliza en la definicin del parmetro '%s', "
                                        + "pero ste ltimo no se ha definido realmente.\n\n",
                                nombrePar, parVinc[j]);
                    } else if (posicion == i) {
                        errores += String.format("<Error> El parmetro '%s' no puede depender de s mismo.\n\n",
                                nombrePar);
                    } else if (posicion < i) {
                        errores += String.format("<Advertencia> El parmetro '%s' depende del parmetro '%s', "
                                + "por lo que debe definirse despus de l.\n\n", parVinc[j], nombrePar);
                    }
                }
                for (int j = 0; j < procVinc.length; j++) {
                    if (!vectorProcesos.contains(procVinc[j])) {
                        errores += String.format(
                                "<Error> El parmetro '%s' se utiliza en la definicin del proceso '%s', "
                                        + "pero ste ltimo no se ha definido realmente.\n\n",
                                nombrePar, procVinc[j]);
                    }
                }
            }
            // Comprobar que, dentro de los procesos, procesosVinculados usen
            // nombres definidos y estn en el orden apropiado.
            for (int i = 0; i < this.procesos.length; i++) {
                String nombreProc = this.procesos[i].getNombre();
                String[] procVinc = this.procesos[i].getProcesosVinculados();
                for (int j = 0; j < procVinc.length; j++) {
                    int posicion = vectorProcesos.indexOf(procVinc[j]);
                    if (posicion == -1) {
                        errores += String.format(
                                "<Error> El proceso '%s' se utiliza en la definicin del proceso '%s', "
                                        + "pero ste ltimo no se ha definido realmente.\n\n",
                                nombreProc, procVinc[j]);
                    } else if (posicion == i) {
                        errores += String.format("<Error> El proceso '%s' no puede depender de s mismo.\n\n",
                                nombreProc);
                    } else if (posicion < i) {
                        errores += String.format(
                                "<Advertencia> El proceso '%s' depende del proceso '%s', "
                                        + "por lo que debe definirse despus de l.\n\n",
                                procVinc[j], nombreProc);
                    }
                }
            }

            // Recuperar el elemento "poblacion" y crear el objeto correspondiente
            Element elementoPoblacion = elementoEpidemia.element("poblacion");
            this.poblacion = new Poblacion();
            this.poblacion.cargarDesdeXML(elementoPoblacion);

            // Iterar para recuperar todos los elementos "compartimento" y crear los objetos
            // Comprobar que el nmero, nombre y orden de los compartimentos corresponda con las categoras
            this.compartimentos = new Compartimento[elementoEpidemia.elements("compartimento").size()];
            String[] segunDivisiones = this.combinarCategorias(null);
            if (segunDivisiones.length != this.compartimentos.length) {
                errores += "<Error> Los compartimentos definidos en el archivo abierto no se corresponden "
                        + "con la verdadera combinacin de las distintas categoras definidas.";
                return errores;
            }
            indice = 0;
            for (Iterator i = elementoEpidemia.elementIterator("compartimento"); i.hasNext();) {
                Element elementoCompartimento = (Element) i.next();
                this.compartimentos[indice] = new Compartimento();
                this.compartimentos[indice].cargarDesdeXML(elementoCompartimento);
                if (!this.compartimentos[indice].getNombre().equals(segunDivisiones[indice])) {
                    errores += "<Error> Los compartimentos definidos en el archivo abierto no se corresponden "
                            + "con la verdadera combinacin de las distintas categoras definidas.";
                    return errores;
                }
                this.palabrasReservadas.add(this.compartimentos[indice].getNombre());
                indice++;
            }

            // Generar los atajos correspondientes a los compartimentos cargados
            this.generarAtajos();
        }

        // Devolver los errores producidos, cadena vaca si no ha habido
        return errores;
    }

    /**
     * Guarda la informacin de esta epidemia en el archivo pasado como
     * parmetro, siguiendo el esquema del .xsd de la aplicacin.
     * @param archivoDestino El archivo destino donde se guardar el modelo.
     * @throws java.io.IOException Si hay problemas al escribir en disco.
     * @throws org.dom4j.DocumentException Si hay problemas al crear el objeto de tipo rbol.
     * @throws java.lang.Exception Si se produce algn otro problema.
     */
    public void guardarXML(File archivoDestino) throws IOException, DocumentException, Exception {
        // Primero crear el documento dom4j con la informacin del modelo
        Document documento = DocumentHelper.createDocument();
        // Elemento raz epidemia
        Element elementoEpidemia = new DefaultElement("epidemia");
        documento.setRootElement(elementoEpidemia);
        elementoEpidemia.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
        elementoEpidemia.addAttribute("xsi:noNamespaceSchemaLocation", "DelphSim1.18.xsd");
        elementoEpidemia.addAttribute("unidadTiempo", this.unidadTiempo);

        // Elementos parmetros
        if (this.parametros != null) {
            for (Parametro param : this.parametros) {
                Element elementoParametro = param.volcarAXML();
                elementoEpidemia.add(elementoParametro);
            }
        }

        // Elementos procesos
        if (this.procesos != null) {
            for (Proceso proc : this.procesos) {
                Element elementoProceso = proc.volcarAXML();
                elementoEpidemia.add(elementoProceso);
            }
        }

        // Elemento poblacin
        Element elementoPoblacion = this.poblacion.volcarAXML();
        elementoEpidemia.add(elementoPoblacion);

        // Elementos compartimentos
        for (Compartimento comp : this.compartimentos) {
            Element elementoCompartimento = comp.volcarAXML();
            elementoEpidemia.add(elementoCompartimento);
        }

        // Luego crear el formato, stream y escritor de la salida
        OutputFormat formato = OutputFormat.createPrettyPrint();
        formato.setEncoding("UTF-16");
        formato.setIndent("\t");
        formato.setNewLineAfterDeclaration(false);
        formato.setPadText(false);
        formato.setTrimText(true);
        formato.setXHTML(true);
        java.io.OutputStreamWriter salida = new java.io.OutputStreamWriter(
                new java.io.FileOutputStream(archivoDestino), "UTF-16");
        XMLWriter escritor = new XMLWriter(salida, formato);
        // Y escribir
        escritor.write(documento);
        escritor.close();
    }

    /**
     * Construye el modelo del JList de la interfaz en base a los compartimentos
     * de esta epidemia.
     * @return El modelo de la lista de los compartimentos.
     */
    public DefaultListModel construirListaCompartimentos() {
        DefaultListModel listaCompartimentos = new DefaultListModel();
        for (Compartimento comp : this.getCompartimentos()) {
            listaCompartimentos.addElement(comp.getNombre());
        }
        return listaCompartimentos;
    }

    /**
     * Construye el modelo del JList de la interfaz en base a los parmetros
     * de esta epidemia.
     * @return El modelo de la lista de los parmetros.
     */
    public DefaultListModel construirListaParametros() {
        DefaultListModel listaParametros = new DefaultListModel();
        if (this.getParametros() != null) {
            for (Parametro par : this.getParametros()) {
                listaParametros.addElement(par.getNombre());
            }
        }
        return listaParametros;
    }

    /**
     * Construye el modelo del JList de la interfaz en base a los procesos
     * de esta epidemia.
     * @return El modelo de la lista de los procesos.
     */
    public DefaultListModel construirListaProcesos() {
        DefaultListModel listaProcesos = new DefaultListModel();
        if (this.getProcesos() != null) {
            for (Proceso proc : this.getProcesos()) {
                listaProcesos.addElement(proc.getNombre());
            }
        }
        return listaProcesos;
    }

    /**
     * Construye el modelo del JList de la interfaz en base a los resultados
     * de esta epidemia.
     * @return El modelo de la lista de los resultados.
     */
    public DefaultListModel construirListaResultados() {
        DefaultListModel listaResultados = new DefaultListModel();
        if (this.getResultados() != null) {
            for (Resultado res : this.getResultados()) {
                listaResultados.addElement(res.getTitulo());
            }
        }
        return listaResultados;
    }

    /**
     * Mtodo que genera todas las combinaciones de categoras, una por cada
     * divisin, obteniendo todos los compartimentos posibles.
     * @param divisiones Parmetro opcional con las divisiones a combinar. Si
     *                   fuera <i>null</i>, se combinaran las categoras de la
     *                   epidemia que invoca este mtodo.
     * @return Un vector de cadenas de texto, cada elemento representa uno de
     *         los compartimentos generados por combinacin.
     */
    public String[] combinarCategorias(Division[] divisiones) {
        // Si divs es null, se usan las de la epidemia
        if (divisiones == null) {
            divisiones = this.getPoblacion().getDivisiones();
        }
        // Clculo del nmero de secciones a generar por combinacin
        int numeroClasificaciones = 1;
        for (Division division : divisiones) {
            numeroClasificaciones *= division.getCategorias().length;
        }
        String clasificaciones[] = new String[numeroClasificaciones];
        // Combinar una categora de cada divisin
        int repetirNVeces = numeroClasificaciones;
        int repetirMVeces = 1;
        Categoria[] categorias;
        for (int i = 0; i < divisiones.length; i++) {
            categorias = divisiones[i].getCategorias();
            repetirNVeces = repetirNVeces / categorias.length;
            for (int j = 0; j < categorias.length; j++) {
                for (int l = 0; l < repetirMVeces; l++) {
                    for (int k = 0; k < repetirNVeces; k++) {
                        if (clasificaciones[l * (numeroClasificaciones / repetirMVeces) + repetirNVeces * j
                                + k] == null) {
                            clasificaciones[l * (numeroClasificaciones / repetirMVeces) + repetirNVeces * j
                                    + k] = categorias[j].getNombre();
                        } else {
                            clasificaciones[l * (numeroClasificaciones / repetirMVeces) + repetirNVeces * j
                                    + k] += "_" + categorias[j].getNombre();
                        }
                    }
                }
            }
            repetirMVeces = repetirMVeces * categorias.length;
        }
        return clasificaciones;
    }

    /**
     * Patrn 'factory' aplicado a la creacin de analizadores sintcticos
     * matemticos (JEP). Este mtodo crea una nueva instancia del objeto JEP,
     * iniciada con las funciones que estarn disponibles en la aplicacin.
     * Todas las instancias de JEP deben crearse con este mtodo.
     */
    public static JEP CrearDelphSimJEP() {
        // Creamos un JEP vaco
        JEP jep = new JEP();

        // Le aadimos las funciones predefinidas en la librera solicitadas
        jep.addFunction("sin", new Sine());
        jep.addFunction("cos", new Cosine());
        jep.addFunction("tan", new Tangent());
        jep.addFunction("asin", new ArcSine());
        jep.addFunction("acos", new ArcCosine());
        jep.addFunction("atan", new ArcTangent());
        jep.addFunction("atan2", new ArcTangent2());
        jep.addFunction("sinh", new SineH());
        jep.addFunction("cosh", new CosineH());
        jep.addFunction("tanh", new TanH());
        jep.addFunction("asinh", new ArcSineH());
        jep.addFunction("acosh", new ArcCosineH());
        jep.addFunction("atanh", new ArcTanH());
        jep.addFunction("log", new Logarithm());
        jep.addFunction("ln", new NaturalLogarithm());
        jep.addFunction("exp", new Exp());
        jep.addFunction("pow", new Power());
        jep.addFunction("sqrt", new SquareRoot());
        jep.addFunction("abs", new Abs());
        jep.addFunction("mod", new Modulus());
        jep.addFunction("sum", new Sum());
        jep.addFunction("rand", new org.nfunk.jep.function.Random());
        jep.addFunction("round", new Round());
        jep.addFunction("floor", new Floor());
        jep.addFunction("ceil", new Ceil());

        // Distribuciones de probabilidad discretas
        jep.addFunction("Binomial", new JEPBinomial());
        jep.addFunction("BinomialNegativa", new JEPBinomialNegativa());
        jep.addFunction("Hipergeometrica", new JEPHipergeometrica());
        jep.addFunction("Poisson", new JEPPoisson());

        // Distribuciones de probabilidad continuas
        jep.addFunction("Beta", new JEPBeta());
        jep.addFunction("JiCuadrado", new JEPJiCuadrado());
        jep.addFunction("Exponencial", new JEPExponencial());
        jep.addFunction("Gamma", new JEPGamma());
        jep.addFunction("Normal", new JEPNormal());
        jep.addFunction("TStudent", new JEPTStudent());
        jep.addFunction("Uniforme", new JEPUniforme());

        return jep;
    }

    /**
     * Mtodo recursivo que calcula los nombres identificativos de los atajos
     * en base a los compartimentos de esta epidemia. La recursividad comienza
     * con la cadena vaca, divisin cero.
     * @param cadena El nombre del atajo calculado hasta el momento.
     * @param divisionMeLlego Divisin por la que me llego aadiendo categoras
     *                        al atajo.
     */
    private void calcularAtajos(String cadena, int divisionMeLlego) {
        // Desde la divisin por la que llegue hasta la ltima
        String nuevaCadena = "";
        for (int i = divisionMeLlego; i < this.getPoblacion().getDivisiones().length; i++) {
            // Para cada categora de esa divisin
            for (int j = 0; j < this.getPoblacion().getDivisiones()[i].getCategorias().length; j++) {
                // Combino su nombre con la cadena pasada y se aade como atajo
                nuevaCadena = cadena + this.getPoblacion().getDivisiones()[i].getCategorias()[j].getNombre();
                if (nuevaCadena.split("_").length < this.getPoblacion().getDivisiones().length) {
                    if (!this.palabrasAtajos.contains(nuevaCadena)) {
                        this.palabrasAtajos.add(nuevaCadena);
                    }
                }
                // Como no es la ltima, separamos de la siguiente con un guin
                nuevaCadena += "_";
                // Y llamamos recursivamente a esta funcin para cada divisin siguiente
                for (int k = i + 1; k < this.getPoblacion().getDivisiones().length; k++) {
                    this.calcularAtajos(nuevaCadena, k);
                }
            }
        }
    }

    /**
     * Genera los objetos Atajo usando los nombres calculados previamente.
     * @see #calcularAtajos(java.lang.String, int) 
     */
    public void generarAtajos() {
        // Vaciamos el vector, lo recalculamos y creamos los objetos Atajo
        this.palabrasAtajos.clear();
        this.calcularAtajos("", 0);
        this.atajos = new Atajo[this.palabrasAtajos.size()];
        // Para cada uno de ellos, creamos un Atajo con su nombre y pasndole
        // los compartimentos para que sepa calcular su definicin
        for (int i = 0; i < this.palabrasAtajos.size(); i++) {
            this.atajos[i] = new Atajo(this.palabrasAtajos.get(i).toString(), this.getCompartimentos());
        }
    }

    /**
     * Implementacin de la interfaz Cloneable.
     * @return Un clon idntico a este objeto.
     */
    @Override
    public Epidemia clone() {
        int numPars = this.getParametros().length;
        int numProcs = this.getProcesos().length;
        int numComps = this.getCompartimentos().length;
        int numAtaj = this.getAtajos().length;
        int numResu = this.getResultados().length;
        Epidemia clon = new Epidemia();
        clon.setUnidadTiempo(this.getUnidadTiempo());
        clon.setTiempoSimulacion(this.getTiempoSimulacion());
        clon.setPoblacion(this.getPoblacion().clone());
        clon.setParametros(new Parametro[numPars]);
        for (int i = 0; i < numPars; i++) {
            clon.setParametro(this.getParametro(i).clone(), i);
        }
        clon.setProcesos(new Proceso[numProcs]);
        for (int i = 0; i < numProcs; i++) {
            clon.setProceso(this.getProceso(i).clone(), i);
        }
        clon.setCompartimentos(new Compartimento[numComps]);
        for (int i = 0; i < numComps; i++) {
            clon.setCompartimento(this.getCompartimento(i).clone(), i);
        }
        clon.setAtajos(new Atajo[numAtaj]);
        for (int i = 0; i < numAtaj; i++) {
            clon.setAtajo(this.getAtajo(i).clone(), i);
        }
        clon.setResultados(new Resultado[numResu]);
        for (int i = 0; i < numResu; i++) {
            clon.setResultado(this.getResultado(i).clone(), i);
        }
        clon.setPalabrasReservadas((HashSet) this.getPalabrasReservadas().clone());
        clon.setPalabrasAtajos((Vector) this.getPalabrasAtajos().clone());
        return clon;
    }
}