///////////////////////////////////////////////////////////////////////////////
//                                                                             
// JTOpen (IBM Toolbox for Java - OSS version)                                 
//                                                                             
// Filename: JDLibraryList.java
//                                                                             
// The source code contained herein is licensed under the IBM Public License   
// Version 1.0, which has been approved by the Open Source Initiative.         
// Copyright (C) 1997-2001 International Business Machines Corporation and     
// others. All rights reserved.                                                
//                                                                             
///////////////////////////////////////////////////////////////////////////////

package com.ibm.as400.access;

import java.sql.SQLException;
import java.util.StringTokenizer;



/**
<p>This class represents a system library list for database access.
**/
class JDLibraryList
{
  static final String copyright = "Copyright (C) 1997-2001 International Business Machines Corporation and others.";




  public static final String        LIBL_         = "*LIBL";
  public static final String     USRLIBL_         = "*USRLIBL";   /*@D2A*/ 


  private char[]                  indicators_;
  private String                  defaultSchema_;
  private String[]                   list_;
  private String                  libListType_;                                    /*@D2A*/ 


  /**
  Constructor.

  @param  list                The list of libraries.
  @param  defaultSchema       The default SQL schema.
  @param  naming              The naming convention.
  **/

  JDLibraryList (String list, String defaultSchema, String naming) {
    this(list, defaultSchema, naming, null); 
  }
/**
Constructor.

@param  list                The list of libraries.
@param  defaultSchema       The default SQL schema.
@param  naming              The naming convention.
@param  libListType         The type of library list to work with (e.g. *LIBL or *USRLIBL). Default is *LIBL.
**/
  JDLibraryList (String list, String defaultSchema, String naming, String libListType) // @C1C @D2A added library list type
  {
    boolean startsWithComma = false;                              // @E2a
    String incomingList = list;

    // Initialize.
    defaultSchema_ = null;
    if (!"".equals(defaultSchema) || defaultSchema != null)
    {
        if(defaultSchema.length() > 0)
        {
            if (defaultSchema.startsWith("\""))  //@delim3
                defaultSchema_ = defaultSchema;  //@delim3
            else                                 //@delim3
                defaultSchema_ = defaultSchema.toUpperCase ();
        }
    }
    
    // @F1: through v4r5 the system automatically added the default SQL schema
    //      to the front of the library list.  In v5r1 they stopped adding it.
    //      ODBC Customers complained their apps no longer worked so ODBC changed
    //      to add the default SQL schema to the library list.  We will do the same
    //      here because we will eventually have broken customers as well.  The
    //      rules are add the default SQL schema to the front of the library list
    //      (1) for both SQL and system naming, (2) if it isn't already in the
    //      list, (3) only if a default is specified (don't add the userid if SQL
    //      naming).  Note we won't break the comma started list (@E2) since
    //      that applies only if no default SQL schema is specified.  If no default
    //      is specified we don't change the list given to us by the user.
    if (defaultSchema_ != null)                                      //@F1a
    {                                                                //@F1a
       if ((list != null) && (list.length() > 0))                    //@F1a
       {                                                             //@F1a
          // assume the deafult schema is not in the list            //@F1a
          boolean alreadyInList = false;                             //@F1a
                                                                     //@F1a
          // does something in the list start with the default SQL schema? //@F1a
          if (list.toUpperCase().indexOf(defaultSchema_) >= 0)       //@F1a
          {                                                          //@F1a
             // Since something close is already in the list         //@F1a
             // look at each token for an exact match.  The          //@F1a
             // .indexOf() check will return a false positive if     //@F1a
             // the default SQL schema is DAW and a library in the       //@F1a
             // list is DAWJDBC.  We do this extra processing        //@F1a
             // only if there is a close match for performance       //@F1a
             StringTokenizer tokenizer = new StringTokenizer (list, " ,:;");  //@F1a
             while (tokenizer.hasMoreTokens ())                      //@F1a
             {                                                       //@F1a
                if (tokenizer.nextToken().toUpperCase().equals(defaultSchema_)) //@F1a
                   alreadyInList = true;                             //@F1a
             }                                                       //@F1a
          }                                                          //@F1a
          if (! alreadyInList)                                       //@F1a
             list = defaultSchema_ + "," + list;                     //@F1a
       }                                                             //@F1a
       else                                                          //@F1a
          list = defaultSchema_;                                     //@F1a
    }                                                                //@F1a

    list_ = null;
    int liblPosition = -1;

      //Store the library list type, make sure we have a lib list       //@D2A
      //to work with                                                    //@D2A
      if(libListType != null && libListType.length() > 0){              //@D2A
          if(libListType.equals(LIBL_) || libListType.equals(USRLIBL_)){//@D2A
              libListType_ = libListType;                               //@D2A
          }else{                                                        //@D2A
              libListType_ = LIBL_;                                     //@D2A
          }                                                             //@D2A
      }else{                                                            //@D2A
          //Work with the *LIBL                                         //@D2A
          libListType_ = LIBL_;                                         //@D2A
      }                                                                 //@D2A

    // If a list is specified, then construct the
    // internal list array.
    if (list.length() != 0)
    {

      // OpNav asked that there be a way to specify a library list without
      // getting a default SQL schema.  We agreed that if the library list starts
      // with a comma we would not set a default SQL schema.  We do this
      // only if no schema is in the url.  A schema on the url
      // will be sent as the default SQL schema no matter what is listed
      // in the library list.
      if (defaultSchema_ == null)                // @E2a
      {                                          // @E2a
         String newList = list.trim();           // @E2a
         if (newList.length() > 0)               // @E2a
         {                                       // @E2a
            if (newList.startsWith(","))         // @E2a
               startsWithComma = true;           // @E2a
         }                                       // @E2a
      }

      // Determine if the *LIBL token is included.
      boolean includesLibl =   (list.toUpperCase().indexOf (libListType_) != -1);     /*@D2C*/

      // Parse the list into tokens.
      StringTokenizer tokenizer = new StringTokenizer (list, " ,:;", true);  //@delim2
      String token;
      int count = tokenizer.countTokens ();
      if (includesLibl)
        --count;
      indicators_ = new char[count];
      list_ = new String[count];
      int i = 0;
      while (tokenizer.hasMoreTokens ())
      {
        token = tokenizer.nextToken(); //.toUpperCase();            //@delim2
        
        if((token.compareTo(" ") == 0) || (token.compareTo(",") == 0) || (token.compareTo(":") == 0) || (token.compareTo(";") == 0))      //@delim2
            continue;                                               //@delim2

        if(token.startsWith("\""))                                  //@delim2
        {                                                           //@delim2

            //check if ending quote is in current token
            int nextQuote = token.indexOf("\"", 1);                 //@delim2

            //get next token and search for ending quote
            while(tokenizer.hasMoreTokens() && (nextQuote == -1))   //@delim2
            {                                                       //@delim2
                token += tokenizer.nextToken();                     //@delim2
                nextQuote = token.indexOf("\"", 1);                 //@delim2
            }                                                       //@delim2
        }                                                           //@delim2
        else                                                        //@delim2
        {                                                           //@delim2
            token = token.toUpperCase();                            //@delim2
        }                                                           //@delim2
            

        // Mark the position of *LIBL.  Only mark
        // the first occurence.  The system will
        // return an error later if it occurs
        // more than once.
          if (token.equalsIgnoreCase (libListType_))            /*@D2C*/
        {
          if (liblPosition == -1)
            liblPosition = i;
        }
        else
        {
          if (includesLibl)                                                 //IF *LIBL was in the libraries connection property
            indicators_[i] = (liblPosition == -1) ? 'F' : 'L';
          else if(incomingList.equals("") && defaultSchema != null && !defaultSchema.equals(""))     //if libraries connection property was not specified and a default collection was put on the URL        //@K1A
            indicators_[i] = (i == 0) ? 'F' : 'L';                                                                                                                                  //@K1A
          else
            indicators_[i] = (i == 0) ? 'C' : 'L';
          list_[i] = token;
          ++i;
        }
      }
      
      if(i != count)                                            //@delim2
      {                                                         //@delim2
          String[] tmpList = list_;                             //@delim2
          list_ = new String[i];                                //@delim2
          System.arraycopy(tmpList, 0, list_, 0, i);            //@delim2
      }                                                         //@delim2

      // If no default SQL schema was specified, then
      // derive it from the list.
      // @E1 do this only for SQL naming!  If we tell the system to use a default SQL schema
      //     then the library list is ignored.  That would break some apps using system naming.
      //     For example, suppose the libraries property is "libraries=lib1,lib2,lib3", and the
      //     file is in lib3.  If we take the first one off the list and set it to be the default
      //     (remember the others will be ignored when a default is set) the apps that
      //     used to find a file in lib3 no longer work.  The behavior as now coded
      //     consistent through v5r1.  In v5r2 we changed to make the first
      //     item the default.  The user can override this behavior by starting the
      //     list with a comma.
      if ((defaultSchema_ == null)
          && (! startsWithComma)                         // @E2a don't set default SQL schema if first char is a comma
          && (naming.equals (JDProperties.NAMING_SQL)))  // @E1c @C1C
      {
        //@KBA  Fix for JTOpen Bug 4025 - *LIBL is not added to the list_ array.  Therefore, the first thing in the
        // list_ array should become the default SQL schema, provided that there was a libraries list.
        //@KBD if (liblPosition != 0)
        //@KBD   defaultSchema_ = list_[0];
        //@KBD else if (list_.length > 1)
        //@KBD   defaultSchema_ = list_[1];
          if(list_.length > 0)                   //@KBA
              defaultSchema_ = list_[0];        //@KBA
      }
     
      //if default SQL schema is longer than 10, then it cannot be in the library list
      //users must use SET PATH
      if((list_.length > 0 ) && (list_[0].length() > 10) && (list_[0].equals(defaultSchema_)))//@128sch
      {                                                              //@128sch
          String[] tmpList = list_;                                  //@128sch
          list_ = new String[tmpList.length-1];                      //@128sch
          if(list_.length > 0)
              System.arraycopy(tmpList, 1, list_, 0, list_.length);  //@128sch
         
          if (JDTrace.isTraceOn())  //@128sch
              JDTrace.logInformation (this, "Schema " + defaultSchema_ + " is too long to be in library list by Toolbox (Hostserver will add it), but will still be set as default SQL schema"); //@128sch
      }//@128sch
      
      // Reverse the order of the 'F' libraries, so that
      // they get added to the library in the right order.
      if (liblPosition > 1)
      {
        int halfPoint = ((int) (liblPosition / 2)) - 1;
        String temp;
        for (int j = 0; j <= halfPoint ; ++j)
        {
          int j2 = liblPosition - j - 1;
          temp = list_[j];
          list_[j] = list_[j2];
          list_[j2] = temp;
        }

      }
    }

    // Otherwise, no list was specified.
    else
    {
      list_ = new String[0];
    }
     
  }



/**
Add the libraries in this list to the server job's library
list for the specified connection.  If the caller specified
*LIBL in the list, then add libraries listed before
*LIBL to the front of the server job's library list.
Likewise, add any libraries listed after *LIBL to the
back of the server job's library list.  If *LIBL is not
specified in the list, then all libraries are replaced
in the server job's library list.

@param      connection      Connection to the system.
@param      id              The id.

@exception  SQLException    If an error occurs.
**/
  void addOnServer (AS400JDBCConnection connection, int id)
  throws SQLException
  {
    if (list_.length > 0)
    {

      DBNativeDatabaseRequestDS request = null; //@P0A
      DBReplyRequestedDS reply = null; //@P0A
      try
      {
        request = DBDSPool.getDBNativeDatabaseRequestDS(DBNativeDatabaseRequestDS.FUNCTIONID_ADD_LIBRARY_LIST, id, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA, 0); //@P0C
        request.setListOfLibraries (indicators_, list_, connection.converter_); //@P0C

        reply = connection.sendAndReceive (request); //@P0C

        int errorClass = reply.getErrorClass();
        int returnCode = reply.getReturnCode();

        if (errorClass != 0)                                                 // @D1C
        {
          if ((errorClass == 5) && (returnCode == 1301))                         // @D1A
          {
            // if the error class is NDB and ret code is library not added       @D1A
            //   continue because library being added is already in *LIBL        @D1A
            //   or library does not exist and either case is OK to continue     @D1A
            connection.postWarning(JDError.getSQLWarning(connection, id, errorClass, returnCode));  // @D1A
          }                                                                        // @D1A
          else
          {
            JDError.throwSQLException (this, connection, id, errorClass, returnCode);
          }                                                                  // @D1A
        }                                                                    // @D1A
      }
      catch (DBDataStreamException e)
      {
        JDError.throwSQLException (JDError.EXC_INTERNAL, e);
      }
      finally //@P0A
      {
        if (request != null) { request.returnToPool(); request = null; } //@P0A
        if (reply != null) { reply.returnToPool();  reply = null; } //@P0A // Only getErrorCode used
      }
    }
  }



/**
Get the default SQL schema.

@return     The default SQL schema, or null if none specified.
**/
  String getDefaultSchema ()
  {
    return defaultSchema_;
  }

  /**
   *  Returns the string representation of the object.
   *  @return The string representation.
   **/
   public String toString()
   {
       /*
       * Implementation note: Used only for tracing information.
       */
       
       return "";
   }
}


