package q02b.exe.utils.oracle;



import java.io.OutputStream;

import java.io.InputStream;



import q02b.exe.utils.Q02bConstantes;

import q02b.exe.utils.Q02bFormatData;

import weblogic.jdbc.vendor.oracle.OracleThinBlob;

import weblogic.jdbc.vendor.oracle.OracleThinClob;



import java.sql.Clob;

import java.sql.ResultSet;

import java.sql.Statement;

import java.sql.SQLException;

import java.sql.Connection;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;



import oracle.jdbc.OracleTypes;





/**

 * Custom BC4J DBTransaction implementation.

 * 

 * Works in tandem with a custom DBTransactionFactory implementation

 * which returns instances of this subclass of DBTransactionImpl2 

 * instead of the default one.

 */

public class Q02bBDUtilidades  {

//	 Constants for ORA database error messages related to constraint violations.

	  private static final String CHECKCONS   = "ORA-02290";

	  private static final String FOREIGNKEY  = "ORA-02291";

	  private static final String RESTRICTDEL = "ORA-02292";

	  private static final String UNIQUEKEY   = "ORA-00001";

	  private static final String USERKEY     = "ORA-20001";

	  

	  

  /** 

    * A JboException might wrap a database constraint violation 

    * If this is the case, this method returns the violated constraint name,

    * otherwise it returns null.

    * 

    * We check for the following database error codes:

    * <ul> 

    * <li>ORA-02290: Check constraints</li> 

    * <li>ORA-02291: Foreign Key</li> 

    * <li>ORA-02292: Restricted Delete Foreign Key</li> 

    * <li>ORA-00001: Unique Key</li> 

    * </ul> 

    * 

    * Code inspired by the JHeadstart Team's idea for this feature that we

    * hope to make a base BC4J framework feature in an upcoming release.

    */ 

  public static String dbConstraintViolation( SQLException sqlException) { 

   

    String violatedConstraintName = null;

    // Check whether it is a constraint violation. 

    String sqlMsg = sqlException.getMessage(); 

    if (sqlMsg.startsWith(CHECKCONS)||sqlMsg.startsWith(FOREIGNKEY)||

	      sqlMsg.startsWith(RESTRICTDEL)||sqlMsg.startsWith(UNIQUEKEY) ||

	      sqlMsg.startsWith(USERKEY)) { 

	    // constraint name is prefixed with schema owner, separated by "."

	    int posStart=sqlMsg.indexOf("."); 

	    int posEnd=sqlMsg.indexOf(")",posStart); 

	    violatedConstraintName=sqlMsg.substring(posStart+1,posEnd); 

    } 

     

    return violatedConstraintName; 

  } 

  public static int getOracleType( String tipoJava) { 

	//definición de tipos para cursores

	  int tipo = 0;

	  if ( tipoJava.equals("Integer")){

		  //return java.sql.Types.INTEGER;

		  return OracleTypes.NUMBER;

	  }else if ( tipoJava.equals("Boolean")){

		  return java.sql.Types.BOOLEAN;

	  }else if ( tipoJava.equals("Date")){

		  return java.sql.Types.DATE;

	  }else if ( tipoJava.equals("Double")){

		  return java.sql.Types.DOUBLE;

	  }else if ( tipoJava.equals("String")){

		  return java.sql.Types.VARCHAR;

	  }else if ( tipoJava.equals("ResultSet")){

		  return OracleTypes.CURSOR;

	  }else if ( tipoJava.equals("Clob")){

		  return java.sql.Types.CLOB;

	  }else if (tipoJava.equals("XMLType")){

		  return OracleTypes.OPAQUE;

	  }

	  return tipo;

  }

  /**

 * @param procedureName, nombre del procedimiento almacenado a llamar

 * @param paramCount número de parametros del procedimiento (suma de parametros de entrada

 * y de salida)

 * @return cadena de llamada al procedimiento.

 */

public static String buildProcedureCall(String procedureName, int paramCount) {

      StringBuffer sb = new StringBuffer("{call "+procedureName+"(");

      String CandenaLlamada = null;

      for (int n = 1; n <= paramCount; n++) {

          sb.append("?");

          if (n < paramCount) 

          	{	

        	  sb.append(",");        	  

        	}

          else{

        	  sb.append(")}");

          }

      }

      CandenaLlamada = sb.toString();

      return CandenaLlamada;

  }



	/**

	 * @param functionName, nombre de la función a llamar

	 * @param paramCount número de parametros de la función (suma de parametros de entrada

	 * y de salida)

	 * @return cadena de llamada al procedimiento.

	 */

	public static String buildFunctionCall(String functionName, int paramCount) {

	      StringBuffer sb = new StringBuffer("{? = call "+functionName+"(");

	      String CandenaLlamada = null;

	      for (int n = 1; n <= paramCount; n++) {

	          sb.append("?");

	          if (n < paramCount) 

	          	{	

	        	  sb.append(",");        	  

	        	}

	          else{

	        	  sb.append(")}");

	          }

	      }

	      CandenaLlamada = sb.toString();

	      return CandenaLlamada;

	  }



 /**

 * @param datos, ClOB con los datos de retorno de la ejecución de PL

 * @return chunk, String con la conversión a caracteres de los datos Ascii

 * @throws Exception

 */

public static String tatamientoClob (Clob datos) throws Exception

 {		

		 //se crea un inputSteam con los datos en  Accii del CLOB

		try{	 

		 InputStream is = datos.getAsciiStream();          		

		 //se multiplica el nº de caracteres por 8 para calcular el numero total de Byte

		 //que tienen el resultado. 

		 int tam = new Long(datos.length()).intValue() * 8;            

		 byte[] bufferXml = new byte[tam];

		 int lenght = 0;            

		 while (true) {

		     int size = is.read(bufferXml, lenght, tam);

		     if (size < 0){

		       break;

		     }

		     lenght += size;                                                           

		  }

		 String chunk = new String(bufferXml, 0, lenght);		 

		return chunk;

		}catch(Exception ex){   	   

			java.io.StringWriter sw = new java.io.StringWriter();

			java.io.PrintWriter pw = new java.io.PrintWriter(sw);

			ex.printStackTrace(pw);

			throw ex;

		}

 }



	/**

	 * Debido a que el generador de TablasBD no realiza un EMPTY_BLOB() en la update,

	 * debemos hacerlo nosotros antes de introducir el nuevo documento

	 * 

	 * @param ficheroInputStream - Stream del fichero que se acaba de recoger

	 * @param queryInsert 		 - Query que obtiene el blob

	 * @param queryUpdate		 - Query que obtiene el blob

	 * @param conexion			 - Conexión a Oracle

	 * 

	 * @throws Exception

	 */

	public static void updateFile(InputStream ficheroInputStream, String queryInsert, String queryUpdate, Connection conexion) throws Exception

	{

		try

		{

			//Se general el Statement

     		Statement stmt 		= conexion.createStatement();

     		//Se comprueba que se realice correctamente el update

     		if(stmt.executeUpdate(queryUpdate)>0)

     		{

     			insertFile(ficheroInputStream, queryInsert, conexion);

     		}

		}

		catch(Exception ex){

			java.io.StringWriter sw = new java.io.StringWriter();

			java.io.PrintWriter  pw = new java.io.PrintWriter(sw);

			ex.printStackTrace(pw);

			throw ex;

		}

	}



	/**

	 * Función que inserta un blob en la base de datos. Es NECESARIO que previamente

	 * se haya realizado una insert mediante un EMPTY_BLOB() en dicha tabla.

	 * 

	 * @param ficheroInputStream - Stream del fichero que se acaba de recoger

	 * @param query 			 - Query que obtiene el blob

	 * @param conexion			 = Conexión a Oracle

	 * 

	 * @throws Exception

	 */

	public static void insertFile(InputStream ficheroInputStream, String query, Connection conexion) throws Exception

	{

		try{

			//Se general el Statement

     		Statement stmt 		= conexion.createStatement();

     		//Se lanza la query y se obtiene el ResultSet

     		ResultSet rs 		= stmt.executeQuery(query);

     		//Se avanza una posición el puntero del ResultSet

     		rs.next();

     		//Se obtiene el blob

     		OracleThinBlob b 		= (OracleThinBlob)rs.getBlob(1);

     		//Se obtiene un OutputStream para poder escribir en él

     		OutputStream bStream 	= b.getBinaryOutputStream();

     		//Se cierra el resultSet

     		rs.close();

            //Escribo los datos en el blob 

            int data=0;

            //Se escriben los datos mientras existan

            while((data = ficheroInputStream.read())>-1){

                 bStream.write(data);
            }
            bStream.flush();

            bStream.close(); // MUY IMPORTANTE!!!

            ficheroInputStream.close(); 

		} catch(Exception ex){

			java.io.StringWriter sw = new java.io.StringWriter();

			java.io.PrintWriter  pw = new java.io.PrintWriter(sw);

			ex.printStackTrace(pw);

			throw ex;

		}

	}

	

	/**

	 * Función que inserta un clob en la base de datos. Es NECESARIO que previamente

	 * se haya realizado una insert mediante un EMPTY_CLOB() en dicha tabla.

	 * 

	 * @param parametro			 - String que se insertará en el clob

	 * @param query 			 - Query que obtiene el blob

	 * @param conexion			 = Conexión a Oracle

	 * 

	 * @throws Exception

	 */

	public static void insertClob(String parametro, String query, Connection conexion) throws Exception

	{

		try{

			//Se general el Statement

     		Statement stmt 		= conexion.createStatement();

     		//Se lanza la query y se obtiene el ResultSet

     		ResultSet rs 		= stmt.executeQuery(query);

     		//Se avanza una posición el puntero del ResultSet

     		rs.next();

     		//Se obtiene el Clob

     		OracleThinClob 	c 		= (OracleThinClob)rs.getClob(1);

     		//Se obtiene un OutputStream para poder escribir en él

     		OutputStream 	cStream = c.getAsciiOutputStream();

     		//Se cierra el resultSet

     		rs.close();

     		//Se obtienen los bytes

     		byte[] data = parametro.getBytes();

     		//Se escriben los bytes

     		cStream.write(data,0,data.length);

     		//Se cierra el OutputStream

            cStream.flush();

            cStream.close(); // MUY IMPORTANTE!!!

		} catch(Exception ex){

			java.io.StringWriter sw = new java.io.StringWriter();

			java.io.PrintWriter  pw = new java.io.PrintWriter(sw);

			ex.printStackTrace(pw);

			throw ex;

		}

	}

	

	/**

     * Método que a partir de un HashMap compone la where para filtrar las restricciones del usuario.

     * @param mapaRestricciones

     * @param NOMBRE_COL_ENTIDAD Nombre de la columna correspondiente a la entidad.

     * @param NOMBRE_COL_CENTRO Nombre de la columna correspondiente al centro.

     * @param isCentroNull Si es true se indica que la vista puede contener números internos de centro con valor NULL. 

     * En este caso se añadirá en la sql una OR para que en caso de que sea NULL, si cumple con el número interno de 

     * entidad se seleccione el registro.

     * @return String 

     * @throws Exception

     */

    public static String componerWhereRestriccionesEC(HashMap mapaRestricciones,final String NOMBRE_COL_ENTIDAD, final String NOMBRE_COL_CENTRO, boolean isCentroNull) throws Exception {

    	String where = "";

    	if (null!=mapaRestricciones){

			Set set = mapaRestricciones.entrySet();		

			Iterator it = set.iterator();		

			

			where = obtenerRestriccionesEC(it,NOMBRE_COL_ENTIDAD,NOMBRE_COL_CENTRO,isCentroNull);

    	}



		return where.toString();



    }

	

	/**

     * Método que a partir de un HashMap compone la where para filtrar las restricciones del usuario.

     * @param mapaRestricciones

     * @param NOMBRE_COL_ENTIDAD Nombre de la columna correspondiente a la entidad.

     * @param NOMBRE_COL_CENTRO Nombre de la columna correspondiente al centro.

     * @return String 

     * @throws Exception

     */

    public static String componerWhereRestriccionesEC(HashMap mapaRestricciones,final String NOMBRE_COL_ENTIDAD, final String NOMBRE_COL_CENTRO) throws Exception {

    	String where = "";

    	if (null!=mapaRestricciones){

			Set set = mapaRestricciones.entrySet();		

			Iterator it = set.iterator();		

			

			where = obtenerRestriccionesEC(it,NOMBRE_COL_ENTIDAD,NOMBRE_COL_CENTRO,false);

    	}



		return where.toString();



    }

    

    /**

     * Método que compone la where para filtrar las restricciones del usuario.

     * @param it Iterator que contiene le HashMap con las restricciones

     * @param NOMBRE_COL_ENTIDAD Nombre de la columna correspondiente a la entidad.

     * @param NOMBRE_COL_CENTRO Nombre de la columna correspondiente al centro.

     * @param isCentroNull Si es true se indica que la vista puede contener números internos de centro con valor NULL. 

     * En este caso se añadirá en la sql una OR para que en caso de que sea NULL, si cumple con el número interno de 

     * entidad se seleccione el registro.

     * @return

     * @throws Exception

     */

    public static String obtenerRestriccionesEC(Iterator it,final String NOMBRE_COL_ENTIDAD, final String NOMBRE_COL_CENTRO, boolean isCentroNull) throws Exception{

    	StringBuffer where = new StringBuffer();

    	if (it.hasNext()){

			Map.Entry e = (Map.Entry)it.next();

			where.append(" AND ((").append(NOMBRE_COL_ENTIDAD).append("=").append(e.getKey().toString());

			ArrayList listaCentros = (ArrayList)e.getValue();

			if (!listaCentros.isEmpty()){

				where.append(" AND ");

				if (isCentroNull){

					where.append("(");

				}

				where.append(NOMBRE_COL_CENTRO).append(" IN (");

				where.append(Q02bFormatData.obtenerListaElementos(listaCentros,Q02bConstantes.SEPARADOR_LISTA));

				where.append(")");

				if (isCentroNull){

					where.append(" OR ").append(NOMBRE_COL_CENTRO).append(" IS NULL)");

				}

			}

			where.append(")");

			while (it.hasNext()){

				e = (Map.Entry)it.next();

				where.append(" OR (").append(NOMBRE_COL_ENTIDAD).append("=").append(e.getKey().toString());

    			listaCentros = (ArrayList)e.getValue();

    			if (!listaCentros.isEmpty()){

    				where.append(" AND ");

    				if (isCentroNull){

    					where.append("(");

    				}

    				where.append(NOMBRE_COL_CENTRO).append(" IN (");

    				where.append(Q02bFormatData.obtenerListaElementos(listaCentros,Q02bConstantes.SEPARADOR_LISTA));

    				where.append(")");

    				if (isCentroNull){

    					where.append(" OR ").append(NOMBRE_COL_CENTRO).append(" IS NULL)");

    				}

    			}

    			where.append(")");

			}

			where.append(")");

		}

    	return where.toString();

    }

    

    /**

     * Método que a partir de un HashMap compone la where para filtrar las restricciones del usuario para entidades.

     * @param mapaRestricciones

     * @param NOMBRE_COL_ENTIDAD Nombre de la columna correspondiente a la entidad.     

     * @return String 

     * @throws Exception

     */

    public static String componerWhereRestriccionesEntidades(HashMap mapaRestricciones,final String NOMBRE_COL_ENTIDAD) throws Exception {

    	StringBuffer where = new StringBuffer();

    	

    	if (null!=mapaRestricciones){

			Set set = mapaRestricciones.entrySet();		

			Iterator it = set.iterator();		

			

			if (it.hasNext()){

				Map.Entry e = (Map.Entry)it.next();

				where.append(" AND (").append(NOMBRE_COL_ENTIDAD).append("=").append(e.getKey().toString());

				while (it.hasNext()){

					e = (Map.Entry)it.next();

					where.append(" OR ").append(NOMBRE_COL_ENTIDAD).append("=").append(e.getKey().toString());	    				    				    			

				}

				where.append(")");

			}

    	}



		return where.toString();



    }

    

    /**Método que comprobara si se ha producido una excepción debido a checkconsViolation

     * @param sqlException

     * @return true si el mensaje de error es por la violación de la constraint CHECKCONS

     */

    public static boolean checkconsViolation (SQLException sqlException) 

    {

    	boolean violatedConstraint = false;

    	// se recupera el mensaje de la excepcion 

    	String sqlMsg = sqlException.getMessage(); 

    	if (sqlMsg.startsWith(CHECKCONS))

    	{

    		violatedConstraint = true;

    	}

    	return violatedConstraint;

    }

    

    /**Método que comprobara si se ha producido una excepción debido a foreingnkeyViolation

     * @param sqlException

     * @return true si el mensaje de error es por la violación de la constraint FOREIGNKEY

     */

    public static boolean foreingnkeyViolation (SQLException sqlException) 

    {

    	boolean violatedConstraint = false;

    	// se recupera el mensaje de la excepcion 

    	String sqlMsg = sqlException.getMessage(); 

    	if (sqlMsg.startsWith(FOREIGNKEY))

    	{

    		violatedConstraint = true;

    	}

    	return violatedConstraint;

    }

    

    /**Método que comprobara si se ha producido una excepción debido a restrictdelViolation

     * @param sqlException

     * @return true si el mensaje de error es por la violación de la constraint RESTRICTDEL

     */

    public static boolean restrictdelViolation (SQLException sqlException) 

    {

    	boolean violatedConstraint = false;

    	// se recupera el mensaje de la excepcion 

    	String sqlMsg = sqlException.getMessage(); 

    	if (sqlMsg.startsWith(RESTRICTDEL))

    	{

    		violatedConstraint = true;

    	}

    	return violatedConstraint;

    }

    

    /**Método que comprobara si se ha producido una excepción debido a uniquekeyViolation

     * @param sqlException

     * @return true si el mensaje de error es por la violación de la constraint UNIQUEKEY

     */

    public static boolean uniquekeyViolation (SQLException sqlException) 

    {

    	boolean violatedConstraint = false;

    	// se recupera el mensaje de la excepcion 

    	String sqlMsg = sqlException.getMessage(); 

    	if (sqlMsg.startsWith(UNIQUEKEY))

    	{

    		violatedConstraint = true;

    	}

    	return violatedConstraint;

    }

    

    /**Método que comprobara si se ha producido una excepción debido a userkeyViolation

     * @param sqlException

     * @return true si el mensaje de error es por la violación de la constraint USERKEY

     */

    public static boolean userkeyViolation (SQLException sqlException) 

    {

    	boolean violatedConstraint = false;

    	// se recupera el mensaje de la excepcion 

    	String sqlMsg = sqlException.getMessage(); 

    	if (sqlMsg.startsWith(USERKEY))

    	{

    		violatedConstraint = true;

    	}

    	return violatedConstraint;

    }

}