//  Source File Name: DB2Udf.java  1.5
//  
//   Licensed Materials -- Property of IBM 
//  
//   (c) Copyright International Business Machines Corporation, 1996, 1999. 
//       All Rights Reserved. 
//  
//   US Government Users Restricted Rights - 
//   Use, duplication or disclosure restricted by 
//   GSA ADP Schedule Contract with IBM Corp. 

//   Sample program - Java User-defined Functions 

//  Steps to set up the server side: 
//  (1) create and populate the SAMPLE database (db2sampl) 
//  (2) compile this Java file (javac DB2Udf.java) 
//  (3) copy the resulting DB2Udf.class file into sqllib/function 

//   NOTES: (1) The jdk11_path database manager configuration parameter must 
//              be set 
//          (2) The CLASSPATH and shared library path environment variables 
//              must be set, as for any JDBC application. 
//          (3) Visit http://www.software.ibm.com/data/db2/java 
//              for current DB2 Java information 

//  Class DB2UdCli implements the sample client (configuration and 
//  execution using JDBC).  Class DB2Udf implements the UDF 
//  method bodies. 

//  DB2Udf uses the LOB support provided by the COM.ibm.db2.app.* package. 
//  Since JDK1.2 also provides LOB support, references to objects of type  
//  Lob, Blob, and Clob must be explicitly type-decorated to compile on a  
//  system with JDK1.2 support. This sample uses the COM.ibm.db2.app.* LOB  
//  support to provide compatibility for both JDK1.1 and JDK1.2 systems. 

//  For more information about this sample, refer to the README file. 

//  For more information on Programming in Java, refer to the  
//  "Programming in Java" section of the Application Development Guide. 

//  For more information on creating and using user-defined functions,  
//  refer to the "Object-Relational Programming" section of the  
//  Application Development Guide. 

//  For more information on building and running Java programs for DB2, 
//  refer to the "Building Java Applets and Applications" section of the  
//  Application Building Guide. 

//  For more information on the SQL language, refer to the SQL Reference. 

import COM.ibm.db2.app.*;
import COM.ibm.db2.jdbc.app.*;
import java.sql.*;
import java.io.*;
import java.net.*;

// ///// 
//  Java user-defined functions are in this class 
// ///// 
class DB2Udf extends UDF
{
  // ///// 
  //  Translations of C UDFs 
  // ///// 

  //  Integer division example. 
  //  Returns NULL if divisor (b) is zero, otherwise returns plain quotient. 
  public void
  divid (int a, int b, int result) throws Exception
  {
    if (b == 0)
      return;
    else
      set (3, a / b);
  }


  //  CLOB folding example. 
  //  fold() moves n chars from the beginning of a string to its end. 
  //  Since there is no direct access to characters in a CLOB, 
  //  the JDK 1.1 Reader/Writer classes are used. 
  public void
  fold (COM.ibm.db2.app.Clob input, int foldPoint, COM.ibm.db2.app.Clob result) throws Exception
  {
    //  Create a new CLOB to receive output 
    COM.ibm.db2.app.Clob resultClob = COM.ibm.db2.app.Lob.newClob ();
    Writer resultWriter = resultClob.getWriter ();

    if (isNull (1) || isNull (2))
        resultWriter.write ("INVALID INPUT".toCharArray ());
    else
      {
	Reader inputReader = input.getReader ();
        inputReader.skip (foldPoint);		//  go to folding point 
        copyClob (inputReader, (int) input.size () - foldPoint, resultWriter);
        inputReader.reset ();           	//  go back to beginning 
        copyClob (inputReader, foldPoint, resultWriter);
      }

    resultWriter.close ();
    set (3, resultClob);
  }

  //  Internal method for copying characters from one CLOB to another 
  private void
  copyClob (Reader input, int count, Writer output) throws Exception
  {
    char[] buffer = new char[1024];
    int copied = 0;
    while (copied < count)
      {
	int readNow = input.read (buffer, 0, count - copied);
	if (readNow < 0)
	  throw new Exception ("end of Reader reached unexpectedly");
        output.write (buffer, 0, readNow);
        copied += readNow;
      }
  }


  //  Find-the-vowel vowel example. 
  //  Return position of first vowel, or signal SQL error. 
  public void
  findvwl (String a, int result) throws Exception
  {
    for (int i = 0; i < a.length (); i++)
      {
	char x = a.charAt (i);
  	char y = Character.toUpperCase (x);
  	if (y == 'A' || y == 'E' || y == 'I' ||
            y == 'O' || y == 'U' || y == 'Y')
  	  {
  	    set (2, i + 1);	//  SQL indexing begins at 1 
  	    return;
  	  }
      }

    //  return failure message 
    setSQLstate ("38700");
    setSQLmessage ("findvwl: No Vowel");
  }


  //  Counter example. 
  //  Demonstrate use of Java instance variables instead of the scratchpad. 
  int counter = 0;		//  instance variable 
  public void
  ctr (int result) throws Exception
  {
    set (1, ++counter);
  }


  //  Example to demonstrate the use of JAVA UDF along with DATALINK  
  //  columns to materialize contents of a DataLinked URL into a BLOB 
  //  This UDF accepts a String argument(which is a URL obtained 
  //  from a DATALINK column) and returns a BLOB(1K) constructed 
  //  from first 1K bytes of the contents of the URL. 
  public void getBlob(String dlnk, COM.ibm.db2.app.Blob result) throws Exception
  {
    final int SIZE = 1000;
 
    COM.ibm.db2.app.Blob resultBlob = COM.ibm.db2.app.Lob.newBlob();

    //  Create a bufferred output stream for writing to result BLOB 
    BufferedOutputStream out = new BufferedOutputStream(
                                   resultBlob.getOutputStream());
   
    //  If input string is zero -length, the result BLOB will be NULL.  
    //  So just return without initializing (which will make result NULL)  
    if (dlnk.length() == 0)
      return;
   
    //  Create a URL object from the input string  
    URL dlnkUrl = new URL(dlnk);

    //  Create a bufferred input stream to read 1K bytes from the 
    //  contents of the URL into the result BLOB 
    BufferedInputStream in = new BufferedInputStream(dlnkUrl.openStream());

    int b;
    for(int count = 0; (count < SIZE) && ((b = in.read()) != -1); count++)
    {
      out.write(b);
      count++;
    }
    in.close();
    out.close();
    set(2, resultBlob);
  }

  // ///// 
  //  Table function examples 
  // ///// 


  //  Directory traversal example. 
  //  Returns a table containing names and characteristics of files 
  //  in directory given as UDF argument.  WARNING - this is only 
  //  an example.  This function can return potentially private  
  //  information about the database server. 
  File startFile = null;
  String fileNames[] = null;	//  list of recorded files 
  int fileIndex = 0;		//  next file to return information on 
  public void
  listDirectory (String path,	                 //  input 
                 String nameOut, String typeOut, //  outputs 
                 String linkOut, int lengthOut)	 //  more outputs 
       throws Exception
  {
    switch (getCallType()) {
	 
    case SQLUDF_TF_FIRST:

      //  do initialization which is independent of input parameters 
      break;

    case SQLUDF_TF_OPEN: 

      //  path may change on OPEN 
      startFile = new File (path.replace ('/', File.separatorChar));
      fileIndex = 0;
      //  List files in given directory 
      fileNames = startFile.list ();
      if(fileNames == null)
	throw new Exception("Cannot list directory " + path);
      break;

    case SQLUDF_TF_FETCH:

      if (fileIndex == fileNames.length)
	{

	  //  Set end-of-file signal and return 
	  setSQLstate ("02000");

	} else {

	  File thisFile = new File (fileNames[fileIndex]);
	  fileIndex++;
	  if (needToSet (2)) set (2, thisFile.getPath ());
	  if (needToSet (3)) set (3, classifyPath (thisFile, startFile));
	  if (needToSet (4)) set (4, thisFile.getAbsolutePath ());
	  if (needToSet (5)) set (5, (int) thisFile.length ());

	}
      break;

    case SQLUDF_TF_CLOSE:
      break;

    case SQLUDF_TF_FINAL:
      break;

    }

  }


  //  Classify this file by guessing whether it is a plain 
  //  file, directory, or a link. 
  private String classifyPath (File child, File parent) throws Exception
  {
    String type = "";

    //  Try to identify a UNIX link by either of following heuristics: 
    //  - if parent path name does not match beginning of child path name 
    //  - if rest of child path name contains directory separators 
    String childPath = child.getAbsolutePath ();
    String parentPath = parent.getAbsolutePath ();
    String childPathBegin = childPath.substring (0, parentPath.length ());

    if (!childPathBegin.equals (parentPath))
      type = "XLINK";
    else if (childPath.length () <= parentPath.length ())
      type = "UPLINK";
    else
      {
	String childPathEnd = childPath.substring (parentPath.length ());
	if (childPathEnd.indexOf (File.separatorChar) > 1)
	    type = "DOWNLINK";
      }

    //  use standard JDK tests 
    if (child.isDirectory ())
      type = type + "DIR";
    else if (child.isFile ())
      type = type + "FILE";
    else
      type = type + "OTHER";

    return type;
  }
}