package org.utplsql.api.outputBuffer;

import oracle.jdbc.OracleConnection;
import org.utplsql.api.Version;
import org.utplsql.api.exception.InvalidVersionException;
import org.utplsql.api.reporter.Reporter;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class OutputBufferProvider {

    /** Returns an OutputBuffer compatible with the given databaseVersion
     * If we are at 3.1.0 or greater, returns an OutputBuffer based upon the information whether the Reporter has Output or not
     *
     * @param databaseVersion
     * @param reporter
     * @param conn
     * @return
     * @throws SQLException
     */
    public static OutputBuffer getCompatibleOutputBuffer(Version databaseVersion, Reporter reporter, Connection conn ) throws SQLException {
       OracleConnection oraConn = conn.unwrap(OracleConnection.class);

       try {
           if (databaseVersion.isGreaterOrEqualThan(new Version("3.1.0"))) {
               if ( hasOutput(reporter, oraConn) ) {
                   return new DefaultOutputBuffer(reporter);
               }
               else {
                   return new NonOutputBuffer(reporter);
               }
           }
       }
       catch ( InvalidVersionException ignored ) { }

       // If we couldn't find an appropriate OutputBuffer, return the Pre310-Compatibility-Buffer
       return new CompatibilityOutputBufferPre310(reporter);
    }

    private static boolean hasOutput( Reporter reporter, OracleConnection oraConn ) throws SQLException {

        String sql = "select is_output_reporter " +
                " from table(ut_runner.get_reporters_list)" +
                " where ? = substr(reporter_object_name, length(reporter_object_name)-?+1)";
        try ( PreparedStatement stmt = oraConn.prepareStatement(sql)) {
            stmt.setQueryTimeout(3);
            stmt.setString(1, reporter.getTypeName());
            stmt.setInt(2, reporter.getTypeName().length());

            try ( ResultSet rs = stmt.executeQuery() ) {
                if ( rs.next() ) {
                    String isReporterResult = rs.getString(1);

                    if ( isReporterResult == null )
                        throw new IllegalArgumentException("The given type " + reporter.getTypeName() + " is not a valid Reporter!");
                    else
                        return isReporterResult.equalsIgnoreCase("Y");
                }
                else
                    throw new SQLException("Could not check Reporter validity");
            }
        }
    }

    private OutputBufferProvider() {
    }
}

@@ -65,24 +65,56 @@
 Connection;%0A
+import oracle.jdbc.OracleTypes;%0A
 import org.u
@@ -255,43 +255,15 @@
 ql.C
-onnection;%0Aimport java.sql.Prepared
+allable
 Stat
@@ -285,25 +285,26 @@
 ava.sql.
-ResultSet
+Connection
 ;%0Aimport
@@ -1600,576 +1600,410 @@
 ql =
- %22select is_output_reporter %22 +%0A                %22 from table(ut_runner.get_reporters_list)%22 +%0A                %22 where ? = substr(reporter_object_name, length(reporter_object_name)-?+1)%22;%0A        try ( PreparedStatement stmt = oraConn.prepareStatement(sql)) %7B%0A            stmt.setQueryTimeout(3);%0A            stmt.setString(1, reporter.getTypeName());%0A            stmt.setInt(2, reporter.getTypeName().length());%0A%0A            try ( ResultSet rs = stmt.executeQuery() ) %7B%0A                if ( rs.next() ) %7B%0A                    String isReporterResult = rs.getString(1);%0A
+%0A                %22declare %22 +%0A                %22   l_result boolean;%22 +%0A                %22begin %22 +%0A                %22   begin %22 +%0A                %22       execute immediate '%22 +%0A                %22       begin %22 +%0A                %22           :x :=' %7C%7C DBMS_ASSERT.SQL_OBJECT_NAME( ? ) %7C%7C '() is of (ut_output_reporter_base);%22 +%0A                %22       end;'%22 +%0A                %22       using out l_result;%22 +
 %0A   
@@ -2019,43 +2019,47 @@
     
+%22
    
- if ( isReporterResult ==
+exception when others then
  null
- )
+;%22 +
 %0A   
@@ -2075,355 +2075,439 @@
     
+%22
    
-     throw new IllegalArgumentException(%22The given type %22 + reporter.getTypeName() + %22 is not a valid Reporter!%22);%0A                    else%0A                        return isReporterResult.equalsIgnoreCase(%22Y%22);%0A                %7D%0A                else%0A                    throw new SQLException(%22Could not check Reporter validity%22);%0A            %7D
+end;%22 +%0A                %22   ? := case l_result when true then 1 else 0 end;%22 +%0A                %22end;%22;%0A%0A        try ( CallableStatement stmt = oraConn.prepareCall(sql)) %7B%0A            stmt.setQueryTimeout(3);%0A            stmt.setString(1, reporter.getTypeName());%0A            stmt.registerOutParameter(2, OracleTypes.INTEGER);%0A%0A            stmt.execute();%0A            int result = stmt.getInt(2);%0A            return result == 1;
 %0A   

Improve hasOutput check to a real PL/SQL type-check