package es.ejie.frmk.infraestructura.tablas;
/*
* The Hypersonic SQL license means that we must include the following, which applies only
* to the files in hsql.jar:
* 
* Copyright (c) 1995-2000 by the Hypersonic SQL Group. All rights reserved.  
* 
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met: 
* 
* + Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 
* + Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 
* + All advertising materials mentioning features or use of this software must display
* the following acknowledgment: "This product includes Hypersonic SQL." 
* + Products derived from this software may not be called "Hypersonic SQL" nor may
* "Hypersonic SQL" appear in their names without prior written permission of the
* Hypersonic SQL Group. 
* + Redistributions of any form whatsoever must retain the following acknowledgment:
* "This product includes Hypersonic SQL." 

* This software is provided "as is" and any expressed or implied warranties, including,
* but not limited to, the implied warranties of merchantability and fitness for a particular
* purpose are disclaimed. In no event shall the Hypersonic SQL Group or its contributors be
* liable for any direct, indirect, incidental, special, exemplary, or consequential damages
* (including, but not limited to, procurement of substitute goods or services; loss of use,
* data, or profits; or business interruption). However caused any on any theory of liability,
* whether in contract, strict liability, or tort (including negligence or otherwise) arising
* in any way out of the use of this software, even if advised of the possibility of such
* damage. 
* 
* This software consists of voluntary contributions made by many individuals on behalf of
* the Hypersonic SQL Group.  
 */
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import javax.naming.NamingException;

import org.apache.commons.beanutils.BasicDynaBean;
import org.apache.commons.beanutils.BasicDynaClass;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.ResultSetDynaClass;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.conectores.conectorJDBC.Q70ConectorJDBC;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.infraestructura.logs.accesos.Q70LogAccesosInterface;
import es.ejie.frmk.infraestructura.logs.base.Q70ConstantesLogs;
import es.ejie.frmk.infraestructura.logs.incidencias.Q70LogIncidenciasInterface;
import es.ejie.frmk.infraestructura.poolCache.Q70ConexionPoolable;
import es.ejie.frmk.infraestructura.poolCache.Q70GestorPool;
import es.ejie.frmk.infraestructura.poolCache.Q70PoolException;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;
import es.ejie.frmk.infraestructura.utilidades.concurrencia.Q70Lock;

/**
 * Clase que implementa las funcionalidades del componente <b>Gestor de Tablas de Códigos</b>, 
 * recuperando una descripción asociada a un código a partir de una tabla estática, en BBDD o en memoria.
 * <p>
 * El mecanismo de funcionamiento es el siguiente:
 * <p>
 * Cuando reciba una petición de un código sobre una tabla, analizará si la tabla es gestionada por el componente.
 * Si es así, se accederá a la tabla usando el origen de datos definido en la configuración para dicha tabla.
 * Para ello:
 * <ul>
 * <li>si es una tabla que deba residir en memoria:</li>
 * <ul>
 * <li>si todavía no existiera , se creará una imagen de la tabla en memoria utilizando el Driver de HypersonicSQL y 
 * desde el origen de datos especificado en el catálogo</li>
 * <li>a continuación, se ejecuta la query solicitada sobre la tabla de memoria.</li> 
 * </ul>
 * <li>si la tabla de datos no ha de residir en memoria, se ejecuta la query solicitada sobre origen de datos 
 * especificado en el catálogo.</li>
 * </ul>
 * 
 */
public abstract class Q70GestorTablasCodigosBase implements Q70GestorTablasCodigosServidorInterface{

	private Q70Catalogo catalogo;
	private Q70GestorPool gpool = Q70GestorPool.getSingleton();
	private int numTablasCargadasEnMemoria = 0;
	
	private Q70Lock lock = null;
	
	/**
	 * @return Returns the lock.
	 */
	protected Q70Lock getLock() {
		return lock;
	}
	
	/**
	 * @param lock The lock to set.
	 */
	protected void setLock(Q70Lock lock) {
		this.lock = lock;
	}


	/**
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosServidorInterface#setCatalog(es.ejie.frmk.infraestructura.tablas.Q70Catalogo)
	 */
	public void setCatalog(Q70Catalogo catalogo) throws Q70GestorTablasException{
		if (!getLock().isBloqueo()) {
			throw new Q70GestorTablasException(this.getClass().toString(),"setCatalog","Se ha entrado por un método que necesita que el bloqueo del componente esté activo.",null);
		}
		this.catalogo = catalogo;
	}

	/** 
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosServidorInterface#getCatalog()
	 */
	public Q70Catalogo getCatalog() {
		return catalogo;
	}

	/**
	* Construye la sentencia "Select" en función de la tabla y condición.
	* <p>
	* Si la tabla es "multilenguajevertical" también tiene en cuenta el idioma.
	*/ 
	private String buildSelect(String idTabla, String nombreTabla, String condicion,String idioma){
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		
		String valor = null;
		String tipo = null;
		String condicion2;
		boolean cond=false;
		String str;
		String sql = "SELECT * FROM " + nombreTabla;
		if (condicion != null) {
			sql += " WHERE " + condicion;
			cond=true;
		}
		if(idioma != null)
		{
			Q70Tabla tabla = catalogo.getTable(idTabla);
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Q70GestorTablasCodigosBase.buildSelect. Nombre fisico: "+idTabla);
			
			//El valor nomCol sirve unicamente para Multilenguaje Vertical y es la columna que marca el idioma
			String nomCol = tabla.getColNameLanguage();
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Q70GestorTablasCodigosBase.buildSelect. nomCol: "+nomCol);
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Q70GestorTablasCodigosBase.tabla.toString(): "+tabla.toString());
			
			if (tabla.getLanguages()!=null){
				valor = (String)tabla.getLanguages().get(idioma);
				if (null==valor)
					valor = (String)tabla.getLanguages().get(Q70Constantes.IDIOMA_DEFECTO);
				tipo = (String) tabla.getLanguages().get(Q70Constantes.TIPO_IDIOMAS);
				if (!cond){
					str=" WHERE " + nomCol;
				}
				else{
					str=" AND " + nomCol;
				}
				if (tipo.equalsIgnoreCase("int")){
					condicion2= str + " = " + valor;
				}
				else{
					condicion2= str + " = '" + valor + "'";
				}
				sql+= condicion2;
			}
		}
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Q70GestorTablasCodigosBase.buildSelect: "+sql);		
		return sql;
	}
	
	/**
	 * Abre una conexión interna
	 * @return Q70ConexionPoolable
	 * @throws Q70PoolException
	 */
	private Q70ConexionPoolable openConnectionInternal() throws Q70PoolException{
		return (Q70ConexionPoolable)gpool.getResource("conexionInternaHSQL");
	}
	
	/**
	 * Obtiene un ArrayList con los registros obtenidos
	 * @param tabla tabla 
	 * @param sql sentencia sql a ejecutar
	 * @param columns nombre físico de las columnas a obtener
	 * @param colsLogicas nombre lógico de las columnas
	 * @return ArrayList 
	 * @throws Q70GestorTablasException
	 * @throws NamingException
	 */
	private ArrayList selectRecords(Q70Tabla tabla, String sql,Vector columns, Vector colsLogicas) throws Q70GestorTablasException,NamingException {
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		
		ResultSet resultset = null;
		Statement statement=null;
		Connection con=null;
		Q70ConexionPoolable inMemoryConnection = null;
		ArrayList al=null;
		String datasource = null;
		StringBuffer sqlCode = new StringBuffer(); 		
		if (!tabla.getLoaded())
			datasource = tabla.getDatasource();
		if (traza.isTraceActive()){
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG,
			"Se ha realizado una petición de selección de registros con los parámetros => datasource: "+datasource+" sql: "+sql+" columns: "+columns);
		}
		try {
			if (datasource==null){ //de memoria
					try {
						inMemoryConnection = openConnectionInternal();
					} catch (Q70PoolException e) {
						throw new Q70GestorTablasException(this.getClass().getName(), "selectRecords", Q70Constantes.ERROR_POOL, e);
					}
					statement = inMemoryConnection.createStatement();
			}
			else{
				//externa
				 con = openExternalConnection(datasource);
				 statement = con.createStatement();
			}
			resultset = statement.executeQuery(sql);
			
			/* Obtenemos los DynaBeans con los resultados en función de la información 
			obtenida del ResultSet así como con los nombres de las columnas lógicas y físicas contra los que tratar
			en el caso de que éstas difieran (Multilenguaje Horizontal)*/
			al = getResult(resultset, columns,colsLogicas);
			
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.INFO,
				"Se han obtenido " + al.size() + " registro(s) como resultado de la petición.");
			}
			sqlCode.append("OK");

		} catch (SQLException sqle) {
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, Q70Constantes.ERROR_SELECCION+" => sql: " +sql+" para las columnas "+columns);
			}
			sqlCode.append(sqle.getSQLState() + " ");
			sqlCode.append(sqle.getErrorCode());			
			throw new Q70GestorTablasException(this.getClass().getName(), "selectRecords", Q70Constantes.ERROR_SELECCION + " SQLState: " + sqle.getSQLState() + " Code: "+ sqle.getErrorCode() + " Q70Mensaje: "+ sqle.getMessage(), sqle);
		}
		finally{
			Q70LogAccesosInterface log = Q70Factoria.getLogAccesos();
			if (datasource!=null){
				try {
					con.close();

				} catch (SQLException sqle) {
					throw new Q70GestorTablasException(this.getClass().getName(), "selectRecords", Q70Constantes.ERROR_SELECCION + " SQLState: " + sqle.getSQLState() + " Code: "+ sqle.getErrorCode() + " Q70Mensaje: "+ sqle.getMessage(), sqle);
					
				}
			}
			else{
				gpool.freeResource("conexionInternaHSQL",inMemoryConnection);		
			}
			if (log.isEnabled())
				//datasource="**datasource**";
				log.log(Q70Constantes.ID_COMPONENTE, datasource, tabla.getidTabla(), sql.replace('\'', ' '),Q70ConstantesLogs.TIPO_ACCESO_LECTURA, sqlCode.toString());
		}
	
		return al;
	}
	
	/**
	 * crea la tabla en memoria
	 * @param tabla tabla a crear en memoria
	 * @param rs resultSet con la estructura de la tabla a crear
	 * @throws Q70GestorTablasException
	 */
	private void createTableInMemory(Q70Tabla tabla, ResultSet rs) throws Q70GestorTablasException{
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		String sql = "";
		Q70ConexionPoolable inMemoryConnection = null;
		int longCol;
		StringBuffer sqlCode = new StringBuffer();
		if (traza.isTraceActive()){
					traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG,
					"Se ha realizado una petición de creación de tabla en memoria => tabla: "+tabla+" nomTabla: "+getNameTableHsql(tabla));
		}
		try {
			ResultSetMetaData rsMetaData = rs.getMetaData();
			int num_columnas = rsMetaData.getColumnCount();
			if (num_columnas > 0) {
				sql = "CREATE TABLE " + getNameTableHsql(tabla) + "(";
			}
			for (int i = 0; i < num_columnas; i++) {
				longCol=0;
				if (i > 0) {
					sql += ",";
				}
				int escala = rsMetaData.getScale(i+1);
				
				String nomCol = rsMetaData.getColumnName(i + 1);
				int intTipoCol = rsMetaData.getColumnType(i + 1);
				String strTipoCol = rsMetaData.getColumnTypeName(i + 1);
				if (strTipoCol.equals("NUMBER") && (escala==0)){
					strTipoCol = "INTEGER";
				}
				else if (strTipoCol.equals("NUMBER")){
					strTipoCol = "FLOAT";
				}
				else if (strTipoCol.equals("LONG")){
					strTipoCol = "BIGINT";
				}			
				else if (strTipoCol.equals("VARCHAR2")){
					strTipoCol = "VARCHAR";
				}
				else if (strTipoCol.equals("DATE")){
					strTipoCol = "VARCHAR";
				}
				else if (strTipoCol.equals("LONG VARCHAR")){
					strTipoCol = "LONGVARCHAR";
				}										
				sql += nomCol + " " + strTipoCol;
				if (intTipoCol == Types.CHAR || intTipoCol == Types.VARCHAR) {
					longCol = rsMetaData.getColumnDisplaySize(i + 1);
					sql += "(" + longCol + ")";
				}
				//añadimos las columnas al objeto Q70Tabla 
				nomCol=nomCol.toLowerCase();
				tabla.setColumn(nomCol, strTipoCol+longCol);
			}
			if (num_columnas > 0) {
				sql += ")";
			}
			
			try {
				inMemoryConnection = openConnectionInternal();
			} catch (Q70PoolException e) {
				if (traza.isTraceActive()){
					traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método createTableInMemory de la clase Q70GestorTablasCodigosBase");
				}
				throw new Q70GestorTablasException(this.getClass().toString(),"createTableInMemory","Se ha producido un error en el método createTableInMemory.",e);

			}

			StringBuffer sb = new StringBuffer(200);
			sb.append(sql);
			PreparedStatement preparedstatement =
				inMemoryConnection.prepareStatement(sb.toString());
			preparedstatement.execute();
			preparedstatement.close();
			if (tabla.getIndices()!=null){
				createIndexInMemory(tabla.getIndices(),getNameTableHsql(tabla),tabla.getDatasource(),inMemoryConnection);
			}
			sqlCode.append("OK");
		} catch (SQLException sqle) {
			sqlCode.append(sqle.getSQLState() + " ");
			sqlCode.append(sqle.getErrorCode());
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, Q70Constantes.ERROR_CREACION_TABLA);
			}					
			throw new Q70GestorTablasException(this.getClass().getName(), "createTableInMemory",Q70Constantes.ERROR_CREACION_TABLA + " SQLState: " + sqle.getSQLState() + " Code: "+ sqle.getErrorCode() + " Q70Mensaje: "+ sqle.getMessage(), sqle);
		}
		finally{
			Q70LogAccesosInterface log = Q70Factoria.getLogAccesos();
			gpool.freeResource("conexionInternaHSQL",inMemoryConnection);			
			if (log.isEnabled())
				log.log(Q70Constantes.ID_COMPONENTE, tabla.getDatasource(), getNameTableHsql(tabla), sql.replace('\'', ' '), Q70ConstantesLogs.TIPO_ACCESO_CREATE,sqlCode.toString());
		}
	}
	
	/**
	 * Crea los índices sobre la Tabla en Memoria
	 * @param nomIndice nombre del índice
	 * @param nomTabla nombre de la tabla sobre la que se crea el índice
	 * @param ds datasource
	 * @param inMemoryConnection conexión interna (hypersonic)
	 * @throws Q70GestorTablasException
	 */
	private void createIndexInMemory(String nomIndice,String nomTabla,String ds,Q70ConexionPoolable inMemoryConnection) throws Q70GestorTablasException{
		String sql = "CREATE INDEX indice ON "+ nomTabla + nomIndice;
		StringBuffer sb = new StringBuffer(200);
		StringBuffer sqlCode = new StringBuffer();
		sb.append(sql);
		try {
			PreparedStatement preparedstatement =
			inMemoryConnection.prepareStatement(sb.toString());
			preparedstatement.execute();
			preparedstatement.close();
			sqlCode.append("OK");
		} catch (SQLException sqle) {
					sqlCode.append(sqle.getSQLState() + " ");
					sqlCode.append(sqle.getErrorCode());
					if (Q70Factoria.getTraza().isTraceActive()){
						Q70Factoria.getTraza().trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, Q70Constantes.ERROR_CREACION_INDICE);
					}					
					throw new Q70GestorTablasException(this.getClass().getName(), "createIndexInMemory",Q70Constantes.ERROR_CREACION_INDICE + " SQLState: " + sqle.getSQLState() + " Code: "+ sqle.getErrorCode() + " Q70Mensaje: "+ sqle.getMessage(), sqle);
		}
		finally{	
			Q70LogAccesosInterface log = Q70Factoria.getLogAccesos();
			if (log.isEnabled())
				log.log(Q70Constantes.ID_COMPONENTE, ds, nomTabla, sql.replace('\'', ' '), Q70ConstantesLogs.TIPO_ACCESO_CREATE,sqlCode.toString());
		}
	}


	/**
	 * Carga los datos de la tabla en memoria
	 * @param nomTabla nombre de la tabla
	 * @param rs resultSet con los datos de la tabla
	 * @throws Q70GestorTablasException
	 */
	private void loadTableInMemory(String nomTabla, ResultSet rs) throws Q70GestorTablasException {
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		String sql = "";
		StringBuffer sqlCode = new StringBuffer();
		Q70ConexionPoolable inMemoryConnection = null; 
		
		if (traza.isTraceActive()){
					traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG,
					"Se ha realizado una petición de inserción de registros en la tabla en memoria:" + nomTabla);
		}
		try {
			inMemoryConnection = openConnectionInternal();
			sql = "INSERT INTO " + nomTabla;
			ResultSetMetaData rsm = rs.getMetaData();
			String columnas = "";
			String valores = "";
			rs.beforeFirst();
			int numCol = rsm.getColumnCount();				
			for(int i = 1; i <= numCol; i++)
			{
				columnas += (String) rsm.getColumnName(i) + ",";
				valores += "?,";
			}
			columnas = columnas.substring(0, columnas.length() - 1);
			valores = valores.substring(0, valores.length() - 1);
			sql += "(" + columnas + ") VALUES (" + valores + ")";
			PreparedStatement preparedstatement =
				inMemoryConnection.prepareStatement(sql);
			sqlCode.append("OK");				
			while(rs.next()) {
				try {
					preparedstatement.clearParameters();
					
					for (int i = 0; i < numCol; i++) {
						 //tratamos las fechas
						 if (rs.getMetaData().getColumnType(i+1) == 91){
						 	String strFecha = rs.getObject(i + 1).toString();
							preparedstatement.setObject(i + 1, strFecha);
						 }else{
							preparedstatement.setObject(i + 1, rs.getObject(i + 1));
						 }
					}
					preparedstatement.execute();
					preparedstatement.close();
				} catch (SQLException sqle1) {
					throw new Q70GestorTablasException(this.getClass().getName(), "loadTableInMemory", Q70Constantes.ERROR_INSERTAR + " SQLState: " + sqle1.getSQLState() + " Code: "+ sqle1.getErrorCode() + " Q70Mensaje: "+ sqle1.getMessage(),sqle1);
				}
			}
		}catch(SQLException sqle){
			sqlCode.append(sqle.getSQLState() + " ");
						sqlCode.append(sqle.getErrorCode());
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, Q70Constantes.ERROR_PREINSERTAR);
			}
			throw new Q70GestorTablasException(this.getClass().getName(), "loadTableInMemory",Q70Constantes.ERROR_PREINSERTAR + " SQLState: " + sqle.getSQLState() + " Code: "+ sqle.getErrorCode() + " Q70Mensaje: "+ sqle.getMessage(), sqle);
		}catch (Q70PoolException poole) {
			throw new Q70GestorTablasException(this.getClass().getName(), "loadTableInMemory",Q70Constantes.ERROR_PREINSERTAR + " Q70Mensaje: "+ poole.getMessage(), poole);
		}
		finally{
			gpool.freeResource("conexionInternaHSQL",inMemoryConnection);
			Q70LogAccesosInterface log = Q70Factoria.getLogAccesos();
			if (log.isEnabled())
				log.log(Q70Constantes.ID_COMPONENTE, "", nomTabla, sql.replace('\'', ' '), Q70ConstantesLogs.TIPO_ACCESO_INSERT,sqlCode.toString());
		}

	}

	/**
	 * MULTILENGUAJE HORIZONTAL. Obtiene el nombre de la columna específica del idioma correspondiente 
	 * al lenguaje (columna física sobre la Base de Datos) y columna genérica dados para multilenguajeHorizontal
	 * Devuelve NULL en el caso de que la columna especificada no sea una columna lógica que defina un idioma
	 * con Multilenguaje Horizontal
	 * @param nTabla nombre de la tabla
	 * @param col nombre de la columna lógica
	 * @param idioma idioma
	 * @return String
	 */
	private String getColumnLanguage(String nTabla, String col, String idioma){
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Entra en la selección del multilenguaje horizontal");
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "nTabla: "+nTabla);
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "col: "+col);
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "idioma: "+idioma);
		
		//Obtenemos los campos Multiidioma de la tabla
		// El hashmap estará formado por los pares: nombre genérico del campo - hashmap formado
	 	// por los pares: idioma - valor del campo en ese idioma
		Map campos = catalogo.getTable(nTabla).getFields();
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Los campos de la tabla son: "+campos);
		
		/* Funcionalidad únicamente informativa. 
		//Comprobamos los lenguajes para los que tiene definido mulitiidioma
		Iterator it= campos.keySet().iterator();
		while(it.hasNext()){
			System.out.println("Clave: "+it.next().toString());
		}
		*/
		
		//Inicializamos el nombre de la columna física a "null"
		/* Únicamente se actualizará en el caso de que la columna especificada mediante el 
		parámetro "col" sea una columna indicativa de un idioma con Multilenguaje Horizontal
		*/
		String columna=null;
		/*Comprobamos si la columna indica Multilenguaje horizontal.
			Generalmente viene en Mayúsculas. Para evitar posibles errores, buscamos por:
				1.- Según viene indicado en el nombre de la columna
				2.- Pasando en nombre a mayúsculas
				3.- Pasando el nombre a minúsculas
		*/
		HashMap hm = (HashMap)campos.get(col);
		if (hm==null){
			//Lo pasamos a mayúsculas
			hm = (HashMap)campos.get(col.toUpperCase());
			if (hm==null){
				//Lo pasamos a minúsculas
				hm = (HashMap)campos.get(col.toLowerCase());
			}
		}

		//Comprobamos que alguna de las combinaciones del nombre de la columna refleja que 
		//ésta es un nombre simbólico para Multilenguaje Horizontal				
		if (hm!=null){
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "La columna "+col+" indica MultiLenguajeHorizontal");
			//Obtenemos la columna física asociada al idioma recibido como parámetro
			columna = (String)hm.get(idioma);
			if (columna!=null){
				columna=columna.toLowerCase();
			}
			else{
				columna = ((String)hm.get(Q70Constantes.IDIOMA_DEFECTO)).toLowerCase();
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.WARN, "La columna correspondiente al idioma '"+idioma+"' no se encuentra en el fichero de configuración de tablas, por lo que obtenemos la columna por defecto (castellano): "+ columna);			
			}
			
		}
		
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "La columna del idioma "+idioma+" es: "+columna);
		return columna;
	}
	
	/**
	 * Obtiene el nombre físico de un alias pasado como parámetro
	 * @param nomTabla nombre del alias de la tabla
	 * @return String
	 */
	private String getNameTable(String nomTabla){
			Iterator it = catalogo.getTablas().entrySet().iterator();
			boolean encontrado=false;
			while (it.hasNext() && !encontrado){
				Map.Entry e = (Map.Entry)it.next();
				String nTabla = (String)e.getKey();
				Q70Tabla tabla = (Q70Tabla)e.getValue();
				if (nomTabla.equals(tabla.getAlias())){
						encontrado=true;
						return nTabla;
				}			
			}
			return nomTabla;
	}

	/**
	 * Crea y carga en memoria la tabla pasada como parámetro
	 * @param tabla tabla a crear en memoria
	 * @throws Q70GestorTablasException
	 * @throws NamingException
	 */
	private void loadTableProcess (Q70Tabla tabla) throws Q70GestorTablasException, NamingException {
			String ds = tabla.getDatasource();
			Statement statement=null;  
			Connection con=null;
			try {			
				//1. obtiene los registros externos (todos)
				con = openExternalConnection(ds);
				statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
				ResultSet resTabla = statement.executeQuery(buildSelect(tabla.getidTabla(),tabla.getNombreTablaFisico(), null, null));
				
				//	2.- cargar estructura en memoria
				createTableInMemory(tabla, resTabla);
				
				//	3.- cargas datos en memoria
				loadTableInMemory(getNameTableHsql(tabla), resTabla);
				resTabla.close();			
			} catch (SQLException sqle) {
				if (Q70Factoria.getTraza().isTraceActive()){
					Q70Factoria.getTraza().trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR,Q70Constantes.ERROR_SELECCION_EXT);
				}
				throw new Q70GestorTablasException(this.getClass().getName(), "loadtableprocess", Q70Constantes.ERROR_SELECCION_EXT + " SQLState: " + sqle.getSQLState() + " Code: "+ sqle.getErrorCode() + " Q70Mensaje: "+ sqle.getMessage(),sqle);
			}finally{
				try {
					if (!con.isClosed()){
						con.close();
					}
				} catch (SQLException e) {
					throw new Q70GestorTablasException(this.getClass().getName(), "loadtableprocess", Q70Constantes.ERROR_SELECCION_EXT + " SQLState: " + e.getSQLState() + " Code: "+ e.getErrorCode() + " Q70Mensaje: "+ e.getMessage(),e);				}
			}

			//	4.- setLoaded=true
			tabla.setLoaded(Q70Constantes.cierto);
	}
	
	/**
	 * Crea y carga la tabla en memoria de manera síncrona
	 * @param tabla 
	 * @param nombreFisico 
	 * @throws Q70GestorTablasException
	 * @throws NamingException
	 */
	private synchronized String preprocessSelectRecords(Q70Tabla tabla, String nombreFisico) throws Q70GestorTablasException,NamingException{
		String aliasAux;
		if (tabla.getLoaded()){
			aliasAux = this.getNameTableHsql(catalogo.getTable(tabla.getidTabla()));
		}else{
			if (!isSobrepasadoMaximoTablasEnMem()){
				aliasAux = this.getNameTableHsql(catalogo.getTable(tabla.getidTabla()));
				numTablasCargadasEnMemoria++;
				loadTableProcess(tabla);
			}else{
				aliasAux = nombreFisico;
			}
		}
		return aliasAux;
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosInterface#selectRecordsFromTable(java.lang.String, java.lang.String, java.lang.String, java.util.Vector)
	 */
	public ArrayList selectRecordsFromTable(String idTabla,	String condicion, String idioma, Vector columns) throws Q70GestorTablasException, Exception {
		getLock().entradaZonaCritica();
		try {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG,
				"Se ha realizado una petición de selección de registros para los registros: "+columns+" de la tabla con identificador: "+idTabla+" con la condición: "+condicion+" en el idioma: "+idioma);
			}
			String alias=null;
			String nombreFisico=null;
			// Consulto el nombre de la tabla dentro del catalogo.
			Q70Tabla tabla = getTable(idTabla);
			/*  Cargo en una variable auxiliar las columnas físicas reales sobre las que hacer la consulta (MULTILENGUAJE_HORIZONTAL)
			por si hubiera MULTILENGUAJE_HORIZONTAL--> diferencia entre columnas físicas de BBDD y lógicas accesibles desde código */
			Vector colsFisicas = new Vector();
				
				
			if (tabla != null) {
				nombreFisico = tabla.getNombreTablaFisico();
				//El nombre de la tabla es correcto. 
				//Analizamos si se trata de MultilenguajeHorizontal para determinar la columna del lenguaje a utilizar
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "selectRecordsFromTable");
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "nombreTabla: "+nombreFisico);
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "condicion: "+condicion);
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "columns: "+columns);
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "idioma: "+idioma);
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "tabla.getMultilenguaje()"+tabla.getMultilenguaje());
				
				//=============================== MODIFICACIONES PARA MULTILENGUAJE_HORIZONTAL
				if( tabla.getMultilenguaje() != null){
					
					/*La tabla es multilenguaje. Pasamos a analizar si se trata de MULTILENGUAJE_HORIZONTAL.
						No se pone en el mismo IF para que no de una excepción en el caso de que tabla.getMultilenguaje() == null
					*/
					if( tabla.getMultilenguaje().equals(Q70Constantes.MULTILENGUAJE_HORIZONTAL) ){
						traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "La tabla tiene MultilenguajeHorizontal");
						
						//Analizamos si alguna de las columnas se trata de un nombre lógico
						for(int i=0;i<columns.size();i++){
							//getColumnLanguage(String nTabla, String col, String idioma)
							traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Procesando la columna: "+columns.elementAt(i));
							String columnLanguaje = getColumnLanguage(tabla.getidTabla(), columns.elementAt(i).toString(), idioma);
							
							//Revisamos si la columna tiene MultilenguajeHorizontal
							if(columnLanguaje != null){
								//La columna tiene multilenguaje horizontal. Debemos cambiar la columna lógica por la física para hacer la consulta a la BBDD
								traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "La columna tiene multilenguaje horizontal. Modificamos el vector de columnas con el nombre físico");
								traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "La columna correspondiente al lenguaje: "+idioma+"es: "+columnLanguaje);
								traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Valor original: "+columns.elementAt(i).toString()+" ------------ nuevo valor: "+columnLanguaje);
								//Modificamos el vector de columnas fisicas
								colsFisicas.add(columnLanguaje);
								traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Valor modificado");
							}
							else{
								//No es multileng-horiz tomamos el mismo valor de las columnas lógicas para la física. Son equivalentes
								colsFisicas.add(columns.elementAt(i));
							}
						}//for
						
						traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Columnas a obtener tras el procesamiento: "+colsFisicas.toString());
					}
					else{
					//No tiene Multilenguaje Horizontal. Las columnas fisicas y las logicas son las mismas
					colsFisicas = columns;
				}
				}
				else{
					//No tiene Multilenguaje (ni horizontal ni vertical). Las columnas fisicas y las logicas son las mismas
					colsFisicas = columns;
				}
				
				//===============================
				
				//Revisamos si la tabla se encuentra en memoria
				if (tabla.getLoaded()){
					alias = this.getNameTableHsql(catalogo.getTable(tabla.getidTabla()));
				}else{
					if (isTablaDeMemoria(tabla)){
						alias = preprocessSelectRecords(tabla, nombreFisico);
					}else{
						alias = nombreFisico;
					}
				}
			}
			else{
				if (traza.isTraceActive()){
					traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR,Q70Constantes.ERROR_CATALOGO+ "=> identificador de tabla: "+idTabla);
				}
				throw new Q70GestorTablasException(this.getClass().getName(), "selectRecordsFromTable",Q70Constantes.ERROR_CATALOGO, new Exception(Q70Constantes.ERROR_CATALOGO));
			}
			
			/* Obtenemos los DynaBeans con los resultados especificados. Las propiedades
				estarán "indexadas" (get[NombrePropiedad]) con las columnas lógicas independientemente
				de que tenga multilenguaje horizontal o no.
			*/
			//ArrayList resultados = selectRecords(tabla,buildSelect(idTabla,nombreFisico, condicion,idioma), colsFisicas, columns);
			ArrayList resultados = selectRecords(tabla,buildSelect(tabla.getidTabla(),alias, condicion,idioma), colsFisicas, columns);
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Rtdos fisicos obtenidos: "+resultados.toString());
			
			return resultados;
		} catch (RuntimeException e) {
			throw e;
		} finally {
			getLock().salidaZonaCritica();
		}
	}

	/**
	 * comprueba si la tabla es de memoria y no se ha superado el número máximo de tablas que pueden 
	 * cargarse en memoria
	 * @param tabla tabla a consultar
	 * @return boolean
	 */
	private boolean isTablaDeMemoria(Q70Tabla tabla) {
		return tabla.getInMem() && !isSobrepasadoMaximoTablasEnMem();
	}
	
	/**
	* Comprueba si se ha superado el número máximo de tablas que pueden cargarse en memoria;
	* por defecto, devolverá false
	* @return boolean
	*/
	protected boolean isSobrepasadoMaximoTablasEnMem() {
		return false;
	}

	/**
	 * En base al ResultSet con la información obtenido de la consulta a la BBDD genera un ArrayList
	 * de DynaBeans en la que cada uno de los elementos contiene las propiedades:
	 *	- ColumnaLógica (equiv a la columna física si no se trata de Multilenguaje Horizontal)
	 *	- Valor de la propiedad de la BBDD
	 * @param rs resultSet con la información de la consulta sql
	 * @param columns vector de columnas
	 * @param colsLogicas vector de columnas lógicas
	 * @return ArrayList
	 * @throws SQLException
	 * @throws Q70GestorTablasException
	 */
	private ArrayList getResult(ResultSet rs, Vector columns, Vector colsLogicas) throws SQLException, Q70GestorTablasException{	
		
		/* Convertimos los resultados de la consulta a la BBDD depositados en el ResultSet ResultSetDynaClass 
			que viene a ser un ResultSet con DynaBeans en las que sus propiedades son las mismas que las del 
			ResultSet (Nombre propiedad = Nombre columna física de BBDD)
		*/
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);
		DynaProperty properties[] = rsdc.getDynaProperties();
		//BasicDynaClass bdc = new BasicDynaClass(Q70Constantes.nomDynaBean, BasicDynaBean.class, properties);
		
		//================= PARA MULTILENGUAJE HORIZONTAL (diferentes cols fisicas y logicas)
		//Añadimos propiedades para generar el DynaClass con el nombre de la propiedad 
		// correspondiente al nombre lógico de la columna
		DynaProperty propertiesLogic[] = rsdc.getDynaProperties();
		DynaProperty currentProperty = null;
		for(int i=0;i<propertiesLogic.length;i++){
			currentProperty = propertiesLogic[i];
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Nombre de la propiedad "+i+": "+currentProperty.getName());
			//Construimos una propiedad con el nombre logico para el tipo fisico real
			int indexOfProperty = columns.indexOf(currentProperty.getName());
			//Vemos si es necesario el mapeo de propiedades --> Se trata de de una columna 
			// recibida como parámetro en el campo columns, con lo que se utilizará por el código
			// con el nombre lógico.
			if(indexOfProperty != -1){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Realizamos el mapeo de la propiedad fisica "+columns.elementAt(indexOfProperty)+" por la propiedad logica: "+colsLogicas.elementAt(indexOfProperty));
				//Modificamos la propiedad por otra en la que se acceda a los valores a través del nombre lógico ( DynaBean.get[NombreLogico]() )
				currentProperty = new DynaProperty(colsLogicas.elementAt(indexOfProperty).toString(),properties[i].getType());
			}
			
			//Actualizamos las propiedades
			propertiesLogic[i]=currentProperty;
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Añadida propiedad del DynaBean para la columna: "+currentProperty.getName());
		}
		
		//Creamos una BasicDynaClass con las nuevas propiedades en las que el DynaBean almacene
		// los valores en base a los nombres lógicos. Muy importante para aquellos casos en los que
		// se utilice Multilenguaje Horizontal
		BasicDynaClass bdcLogic = new BasicDynaClass(Q70Constantes.nomDynaBean, BasicDynaBean.class, propertiesLogic);
		traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "BasicDynaClass instanciada");
		
		//=================
		Iterator rows = (Iterator)rsdc.iterator();
		ArrayList results = new ArrayList();			
		if (traza.isTraceActive()){
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.INFO,
			"Se ha realizado una petición de tratamiento del resultado");
		}
		
		/*Recorremos los DynaBeans del ResultSetDynaClass para ir generando el 
			ArrayList() que contenga los DynaBeans obtenidos de la consulta pero modificando
			los nombres de las propiedades para que pueda hacerse referencia a sus valores por los 
			nombres lógicos.
			*/
		while (rows.hasNext()) {
				DynaBean oldRowDynaBean = (DynaBean) rows.next();
				DynaBean newRowDynaBean = null;
				try{	
					//================= PARA MULTILENGUAJE HORIZONTAL (diferentes cols fisicas y logicas)			 	
					// ORIGINAL: newRowDynaBean = bdc.newInstance();
					newRowDynaBean = bdcLogic.newInstance();
					traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "BasicDynaClass newRowDynaBean instanciada");
					
				}catch(Exception e){
					if (traza.isTraceActive()){
						traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, Q70Constantes.ERROR_RESULTADO);
					}
					throw new Q70GestorTablasException(this.getClass().getName(), "getResult", Q70Constantes.ERROR_RESULTADO, new Exception(Q70Constantes.ERROR_RESULTADO));
				}
				
				//Si no se han especificado columnas no es necesario ningún procesamiento adicional de los DynaBeans a devolver
				if((columns==null) || (columns.size() == 0))
				{	
					try { 
						PropertyUtils.copyProperties(newRowDynaBean, oldRowDynaBean);
					}catch(Exception e){
						if (traza.isTraceActive()){
							traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, Q70Constantes.ERROR_RESULTADO);
						}
						throw new Q70GestorTablasException(this.getClass().getName(), "getResult", Q70Constantes.ERROR_RESULTADO, new Exception(Q70Constantes.ERROR_RESULTADO));
					}
				}
				else
				{
					// Si se han especificado columnas, es necesario definir el DynaBean con las propiedades lógicas.
					// Recorremos los valores de las columnas para sustituir los nombres físicos por lógicos
					for(int i = 0; i < columns.size(); i++)
					{
					   traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, " Antes de obtener el nombre de la columna a obtener");
					   
					   //Las columnas lógicas y físicas deben estar construidas en orden para que coincidan los índices.
					   String col = (String) columns.get(i);
					   String colLogica = (String) colsLogicas.get(i);
					   
					   traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, " Columna obtenida: "+col);
					   traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, " Columna Lógica obtenida: "+colLogica);
					   traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, " oldRowDynaBean: "+oldRowDynaBean.toString());
					   
					   //newRowDynaBean.set(col, oldRowDynaBean.get(col));
					   traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "Antes de establecer el valor en el DynaBean clave: "+colLogica+" valor: "+oldRowDynaBean.get(col));
					   //Actualizamos únicamente las propiedades indicadas por el parámetro "columns" pero con los nombres lógicos de las propiedades
					   newRowDynaBean.set(colLogica, oldRowDynaBean.get(col));
					}				 	
				}
				results.add(newRowDynaBean);
		}//while()	   
		rs.close();
		return results;		
	}	

	/**
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosServidorInterface#tableExist(java.lang.String)
	 */
	public boolean tableExist(String nombreTabla){
		return (null!=getTable(nombreTabla));
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosServidorInterface#getInfoTable(java.lang.String)
	 */
	public Map getInfoTable(String nombreTable){
		return catalogo.getTable(nombreTable).getInfoTable();
	}

	/**
	 * Abre una conexión externa
	 * @param datasource datasource
	 * @return Connection
	 * @throws SQLException
	 * @throws NamingException
	 */
	private Connection openExternalConnection(String datasource) throws SQLException,NamingException {
		return Q70ConectorJDBC.getSingleton().getConnection(datasource);
	}

	/**
	 * Borra todas las tablas de memoria (las que estuviesen cargadas) 
	 */
	private void dropAllTablesFromMemory(){
		if (null!=catalogo){
			Iterator it = catalogo.getTablas().entrySet().iterator();
			Map.Entry mapTabla;
			Q70Tabla tabla;
			String nombreTabla;
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			Q70LogAccesosInterface log = Q70Factoria.getLogAccesos();
			
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "");
			}
			while (it.hasNext()) {
				mapTabla = (Map.Entry)it.next();
				tabla = (Q70Tabla)mapTabla.getValue();
				if (tabla.getLoaded()){
					//nombreTabla=tabla.getAlias();
					nombreTabla= this.getNameTableHsql(tabla);
					dropTable(nombreTabla);
					if (log.isEnabled()){
						log.log(Q70Constantes.ID_COMPONENTE, tabla.getDatasource(), tabla.getidTabla(),"", Q70ConstantesLogs.TIPO_ACCESO_BORRADO, "OK");
					}
					catalogo.getTable(tabla.getidTabla()).setLoaded("false");
				}
			}
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.INFO, "Han sido borradas todas las tablas de memoria.");
			}
		}			
	}
	
/**
 * El método elimina una Tabla de códigos de memoria.
 * @param nTabla nombre de la Tabla a eliminar de memoria
 */	
	private void dropTable(String nTabla){
		String sql;
		Statement stmt;
		Q70ConexionPoolable inMemoryConnection = null;
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		
		if (traza.isTraceActive()){
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.INFO,
			"Se ha realizado una petición de borrado de la tabla de memoria: " +nTabla);
		}
		sql = "DROP TABLE "+ nTabla;
		try {
			inMemoryConnection = openConnectionInternal();
			stmt = inMemoryConnection.createStatement();
			stmt.execute(sql);
		} catch (SQLException sqle) {
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, sqle.toString());
			}
		}catch (Q70PoolException poole) {
		}
		finally{
			gpool.freeResource("conexionInternaHSQL",inMemoryConnection);
		}
				
	}

	/**
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosServidorInterface#reload(java.lang.String)
	 */
	public void reload(String aTable){
		//String nombre = catalogo.getTable(aTable).getAlias(); 
		String nombre = this.getNameTableHsql(catalogo.getTable(aTable));
		dropTable(nombre);
		catalogo.getTable(aTable).setLoaded("false");
		numTablasCargadasEnMemoria--;
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosServidorInterface#getLoadedTables()
	 */
	public ArrayList getLoadedTables(){
		return catalogo.getLoadedTables();
	}
	
	/**
	 * Obtiene el nombre de la tabla en memoria (hypersonic). Este nombre está formado por el nombre físico
	 * de la tabla seguido de un sufijo
	 * @param aTable tabla a consultar
	 * @return String
	 */
	private String getNameTableHsql(Q70Tabla aTable){
		return aTable.getNombreTablaFisico() + Q70Constantes.SUFIJO_TABLA_HSQL;	 
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#activar()
	 */
	public void activar() {
		getLock().activar();
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#liberar()
	 */
	public void liberar() throws Q70FrameworkException
	{
		if (!getLock().isBloqueo()) {
			throw new Q70FrameworkException(Q70Constantes.ID_COMPONENTE,this.getClass().toString(),"liberar","Se ha entrado por un método que necesita que el bloqueo del componente esté activo.",null);
		}
		dropAllTablesFromMemory();
		//gpool.release();
		//gpool.release("conexionInternaHSQL");
		catalogo = null;
		//doDestruir();
	}
	
	/**
	 * Método que debe sobreescribir la clase que hereda principalmente para
	 * eliminar la referencia a la instancia del componente. 
	 */	
	protected abstract void doDestruir();

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#suspender()
	 */
	public void suspender() throws Q70FrameworkException{	
		try {
			getLock().suspender();
		} catch (Q70FrameworkException e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método suspender de la clase Q70GestorTablasCodigosBase");
			}
			throw new Q70FrameworkException(Q70Constantes.ID_COMPONENTE,this.getClass().toString(),"suspender","Se ha producido un error en el método suspender.",e);
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#destruir()
	 */
	public void destruir() throws Q70FrameworkException{
		try {
			liberar();
			doDestruir();
			activar();
		} catch (Throwable e) {
 			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método destruir de la clase Q70GestorTablasCodigosBase");
			}
			throw new Q70FrameworkException(Q70Constantes.ID_COMPONENTE,this.getClass().toString(),"destruir","Se ha producido un error en el método destruir.",e);				
		}
	}

	/**
	 * Método que obtiene una tabla a partir de su identificador. Si el parámetro es un alias, se muestra una traza
	 * de error indicando que no debe accederse por nombre lógico, sino por el nombre físico de la tabla
	 * @param idTabla
	 * @return devuelve un objeto de tipo Q70Tabla
	 */
	private Q70Tabla getTable(String idTabla){ 
		Q70Tabla tabla = catalogo.getTable(idTabla);  
		if (null==tabla){ 
			tabla = catalogo.getTable(getNameTable(idTabla));
			if (null!=tabla){
				if (Q70Factoria.getTraza().isTraceActive()){
					Q70Factoria.getTraza().trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR,
							"Se ha obtenido la tabla a traves del alias [deprecated]. Evitese usando el nombre logico de la tabla '"+idTabla+"'");
				}
			}
		}
		return tabla;
	}

	/**
	 * @see es.ejie.frmk.infraestructura.tablas.Q70GestorTablasCodigosServidorInterface#getMaximoNumeroTablasEnMemoria()
	 */
	public int getMaximoNumeroTablasEnMemoria() {
		return -1;
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Administrable#inicializar()
	 */
	public void inicializar() throws Q70FrameworkException{
		if (!getLock().isBloqueo()) {
			throw new Q70FrameworkException(Q70Constantes.ID_COMPONENTE,this.getClass().toString(),"inicializar","Se ha entrado por un método que necesita que el bloqueo del componente esté activo.",null);
		}
		numTablasCargadasEnMemoria = 0;
		Q70LogIncidenciasInterface logInc = Q70Factoria.getLogIncidencias();
		if (catalogo.getNumTablasEnMemoria()>Q70GestorTablasCodigosServidor.getSingleton().getMaximoNumeroTablasEnMemoria()){
			if (Q70Factoria.getTraza().isTraceActive()){
				Q70Factoria.getTraza().trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR,
						"El numero maximo de tablas por aplicacion es " + Q70GestorTablasCodigosServidor.getSingleton().getMaximoNumeroTablasEnMemoria() + " y se quieren cargar " + catalogo.getNumTablasEnMemoria() + ".");
			}
			if (logInc.isEnabled())
				logInc.log(new Q70GestorTablasException(this.getClass().getName(), "inicializar()", "El numero maximo de tablas por aplicacion es " + Q70GestorTablasCodigosServidor.getSingleton().getMaximoNumeroTablasEnMemoria() + " y se quieren cargar " + catalogo.getNumTablasEnMemoria() + ".",null));
			
		}
	}

	/**
	 * Obtiene el número de tablas cargadas en memoria 
	 * @return int
	 */
	protected int getNumTablasCargadasEnMemoria() {
		return numTablasCargadasEnMemoria;
	}

	/**
	 * Borra la tabla del catálogo (pasados como parámetros)
	 * @param catalogo
	 * @param nombreTabla
	 */
	protected void doRemoveTablaCatalogo(Q70Catalogo catalogo, String nombreTabla) {
		catalogo.removeTable(nombreTabla);
	}

	
}