/**
 *  '$RCSfile$'
 *    Purpose: This class is in order to fix a problem. It doesn't 
 *             has functionality for Metacat.
 *             In Currently, some document in xml_document table 
 *             doesn't have entries in xml_access table. This is 
 *             okay during the old access policy.
 *             But we changed the access policy and if there is no 
 *             entry in xml_access table, except owner, other person 
 *             can not access it. So we need to associate with access 
 *             policy in xml_access table for these doc ids.
 *             The same access policy of these doc' dataset will associate
 *             to them. 
 *  Copyright: 2000 Regents of the University of California and the
 *             National Center for Ecological Analysis and Synthesis
 *    Authors: Jing Tao
 *
 *   '$Author$'
 *     '$Date$'
 * '$Revision$'
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.ucsb.nceas.metacat;


import java.io.*;
import java.util.Vector;
import java.net.URL;
import java.net.MalformedURLException;
import java.sql.*;
import java.util.Stack;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.BufferedWriter;

import org.apache.log4j.Logger;

import edu.ucsb.nceas.metacat.database.DBConnection;
import edu.ucsb.nceas.metacat.database.DBConnectionPool;


/**This class is in order to fix a problem. It doesn't has functionality for 
 *Metacat. 
 *In Currently, some document in xml_document table doesn't have entries in 
 *xml_access table. This is okay during the old access policy.
 *But we changed the access policy and if there is no entry in xml_access table,
 * except owner, other person can not access it. So we need to associate with 
 *access policy in xml_access table for these doc ids. The same access policy 
 *of these docoments' data set will associate to them. 
 */
public class AssociateAccessPolicy {

 
 
  private DBConnection  conn = null;
  private Vector docIdInAccessTable=null;
  private Vector docIdWithoutAccessEntry=null;
  private Vector notFoundDataSetId=null;
  private Vector itsDataSetIdWithouAccessEntry=null;
  private Hashtable docIdMapDataSetId=null; 
  private Logger logMetacat = Logger.getLogger(AssociateAccessPolicy.class);
  
  /**
   * the main routine used to associate access policy
   */
  static public void main(String[] args) 
  {
     DBConnection localConn=null;
     int serialNumber = -1;
     AssociateAccessPolicy policy=null;
     try
     {
      localConn=DBConnectionPool.getDBConnection("AssociateAccessPolict.main");
      serialNumber=localConn.getCheckOutSerialNumber();
      policy = new AssociateAccessPolicy(localConn);
      policy.associateAccess();
      //localConn.close();   
     }//try
     catch (Exception e) 
     {
        System.err.println("Error in AssociateAccessPolicy.main");
        System.err.println(e.getMessage());
        e.printStackTrace(System.err);
     }
     finally
     {
       DBConnectionPool.returnDBConnection(localConn, serialNumber);
     }//finally
     
     if (!(policy.getNotFoundDataSetId()).isEmpty())
     {
         
         System.out.println("Following docid which could not find a mapped" 
                  +" dataset was associated with defualt policy:");
         for (int i=0; i<(policy.getNotFoundDataSetId()).size(); i++)
        {
           String str=(String)(policy.getNotFoundDataSetId()).elementAt(i);
           System.out.println(str);
        }//for
     }//if
     if (!(policy.geItsDataSetIdWithouAccessEntry()).isEmpty())
     {
         
         System.out.println("Following docid which's mapped dataset doesn't has"
                  +" an access entry was associated with defualt policy:");
         for (int i=0; i<(policy.geItsDataSetIdWithouAccessEntry()).size(); i++)
        {
           String str=(String)
                        (policy.geItsDataSetIdWithouAccessEntry()).elementAt(i);
           System.out.println(str);
        }//for
     }//if
  }
  
  /**
   * construct an instance of the DBQuery class 
   *
   * <p>Generally, one would call the findDocuments() routine after creating 
   * an instance to specify the search query</p>
   *
   * @param conn the JDBC connection that we use for the query
   * @param parserName the fully qualified name of a Java class implementing
   *                   the org.xml.sax.XMLReader interface
   */
  public AssociateAccessPolicy( DBConnection conn) 
                  throws IOException, 
                         SQLException, Exception
  {   
    this.conn = conn;
    this.docIdInAccessTable=new Vector();
    this.docIdWithoutAccessEntry=new Vector();
    this.notFoundDataSetId=new Vector();
    this.itsDataSetIdWithouAccessEntry=new Vector();
    this.docIdMapDataSetId=new Hashtable();
    getDocIdInAccessTable();
    getDocIdWithoutAccessEntry();
    getDocIdMapDataSetId();
   }
  
  /**
   * Get the docid which didn't found a dataset id to map it
   */
  public Vector getNotFoundDataSetId()
  {
    return notFoundDataSetId;
  }
  /**
   * Get the docid which it's mapped dataset doesn't has access entry
   */
  public Vector geItsDataSetIdWithouAccessEntry()
  {
    return itsDataSetIdWithouAccessEntry;
  }
 
  /**
   * Get all docIds list in xml_access table
   */
  private void getDocIdInAccessTable()
                      throws SQLException, McdbException,Exception
  {
    PreparedStatement pStmt;
    String docId;
    ResultSet rs=null;
    
    //the query stirng
    String query="SELECT id.docid from xml_access xa, identifier id where xa.guid = id.guid";
    try
    {
      pStmt=conn.prepareStatement(query);
      //excute the query
      pStmt.execute();
      //get the result set
      rs=pStmt.getResultSet();
      //process the result
      while (rs.next())
      {
        
        docId=rs.getString(1);//the result docId 
        //put the result into docIdInAccessTable vetor
        if (!docIdInAccessTable.contains(docId))// delete duplicate docid
        {
          docIdInAccessTable.add(docId);
        }
      }//while
      //close the pStmt
      pStmt.close();
    }//try
    catch (SQLException e)
    {
    	logMetacat.error("Error in getDocidListForDataPackage: "
                            +e.getMessage());
    }//catch
    //System.out.println("docid in access table");
    /*for (int i=0; i<docIdInAccessTable.size(); i++)
    {
         String str=(String)docIdInAccessTable.elementAt(i);
         System.out.println(str);
    }*/
    //for
  }// getDocIdInAccessTable()
  
  /**
   * associateDefaultValue to docid
   * This docid either couldn't find a mapped dataset or it self is a dataset
   * @param docId, the docid which will be associate default access value
   */
  private void associateDefaultValue(String docId)
                      throws SQLException, McdbException,Exception
  {
    PreparedStatement pStmt;
    
    String query=null;
  
    //the query stirng
    //we let accessfileid blank becuause we couldn't know access file
    query="INSERT INTO xml_access " + 
            "(docid, principal_name, permission, perm_type, perm_order)" +
             "VALUES (?,'public','4','allow','allowFirst')";
    
    try
    {
      pStmt=conn.prepareStatement(query);
      //bind value
      pStmt.setString(1, docId);
      //excute the query
      logMetacat.debug("running sql: " + pStmt.toString());
      pStmt.execute();
      pStmt.close();
    }//try
    catch (SQLException e)
    {
      System.out.println("Error in associateDefaultValue: "
                            +e.getMessage());
    }//catch
    
  }// associateDefaultValue
  /**
   * Get all docIds which don't have an entry in xml_access table
   */
  private void getDocIdWithoutAccessEntry()
                                 throws SQLException, McdbException,Exception
  {
    PreparedStatement pStmt=null;
    String docId;
    ResultSet rs=null;

   
    //the query stirng
    String query="SELECT docid from xml_documents";
    try
    {
      pStmt=conn.prepareStatement(query);
      //excute the query
      pStmt.execute();
      //get the result set
      rs=pStmt.getResultSet();
      //process the result
      while (rs.next())
      {
       
        docId=rs.getString(1);//the result docId 
        //System.out.println("docid in document talbe "+docId);
        //If this docId is not in the docIdInAccessTable list,
        //put the result into docIdInAccessTable vetor
        if (!docIdInAccessTable.contains(docId))
        {
           docIdWithoutAccessEntry.add(docId);
        }
     
      }//while
      //close the pStmt
      pStmt.close();
    }//try
    catch (SQLException e)
    {
      pStmt.close();
      logMetacat.error("Error in getDocidListForDataPackage: "
                            +e.getMessage());
    }//catch
    //System.out.println("docid without access entry:");
    /*for (int i=0; i<docIdWithoutAccessEntry.size(); i++)
    {
         String str=(String)docIdWithoutAccessEntry.elementAt(i);
         System.out.println(str);
    }*/
    //for
  }//getDocIdWithoutAccessEntry()  
  
  /**
   * Find dataset docid for these id which doesn't have an entry in access table
   * The access policy of dataset docid will apply the id which doesn't have an
   * an entry in access table.
   * docid and datasetid which will be stored in a hashtable.
   * docid as a key, datasetid as value
   */
  private void getDocIdMapDataSetId()
                              throws SQLException, McdbException,Exception
  {
    
    PreparedStatement pStmt=null;
    ResultSet rs=null;
    String docId=null;
    String dataSetId=null;
    //make sure there is some documents ids which doesn't has an access entry
    if ( docIdWithoutAccessEntry.isEmpty())
    {
      throw new 
          Exception("Every docid in xml_documents table has access policy");
    }
    String query="SELECT docid from xml_relation where subject= ? or object= ?";
    try
    {
      //find a dataset id for those id which doesn't have access entry
      for (int i=0;i<docIdWithoutAccessEntry.size();i++)
      {
        docId=(String)docIdWithoutAccessEntry.elementAt(i);
        pStmt=conn.prepareStatement(query);
        //bind the value to query
        pStmt.setString(1, docId);
        pStmt.setString(2, docId);
        //execute the query
        pStmt.execute();
        rs=pStmt.getResultSet();
        //process the result
        if (rs.next()) //There are some records for the id in docId fields
        {
          dataSetId=rs.getString(1);
          //System.out.println("dataset id: "+dataSetId);
          //put docid and dataset id into hashtable
          docIdMapDataSetId.put(docId, dataSetId);
        }
        else
        {
         //if not found a dataset id, associateput it into notFoundDataSetId
         //and associate with default value
          //System.out.println("the id couldn't find data set id: "+docId);
          associateDefaultValue(docId);
          notFoundDataSetId.add(docId);
        }
        pStmt.close();
      }//for
    }//try
    catch (SQLException e)
    {
      pStmt.close();
      System.out.println("Error ingetDocIdMapDataSetId: "
                            +e.getMessage());
    }
   
  }//getDocIdMapDataSetId()
  
  /**
   * Associate the access policy of dataset to the docid which the data set id
   * mapped
   */ 
   public void associateAccess()
                           throws SQLException, McdbException,Exception
   {
      String docId=null;
      String dataSetId=null;
      String accessFileId=null;
      String principal=null;
      int    permission=0;
      String permType=null;
      String permOrder=null;
      String beginTime=null;
      String endTime=null;
      int    ticketCount=-1;
      PreparedStatement pStmt=null;
      PreparedStatement insertStatement=null;
      ResultSet rs=null;
      String query=null;
      boolean hasRecord=false;
   
      //make sure there is some documents ids which doesn't has access entry
      if ( docIdWithoutAccessEntry.isEmpty())
      {
          throw new 
            Exception("Every docid in xml_documents table has access policy");
      }
      //every docid without access policy couldn't find a dataset to map
      //assign them default access policy value this aleady done in
      //getDocidMapDataSetId
      else if (docIdMapDataSetId.isEmpty())
      {
        
      }
      else
      {
        try
        {
          Enumeration docList = docIdMapDataSetId.keys();
          while (docList.hasMoreElements())
          {
            docId=(String)docList.nextElement();
            dataSetId=(String)docIdMapDataSetId.get(docId);
            query="select accessfileid, principal_name,permission,perm_type,"
                +"perm_order,begin_time,end_time,ticket_count from xml_access xa, identifier id "
                +"where id.docid = ? and id.guid = xa.guid";
            pStmt=conn.prepareStatement(query);
            //bind the value to query
            pStmt.setString(1, dataSetId);
            //excute the query
            pStmt.execute();
            rs=pStmt.getResultSet();
            //every entry for data set id 
            hasRecord=rs.next();
            //couldn't find access entry for dataset the docid mapped
            //assing defualt value to this docid
            if (!hasRecord)
            {
              
              itsDataSetIdWithouAccessEntry.add(docId);
              associateDefaultValue(docId);
            }
            //find the dataset access entry, apply this entry to docid
            else
            {
              
              while (hasRecord)
              {
                //get datasetid's access policy and store them into variables
                accessFileId=rs.getString(1);
                //System.out.println("accessfileid: "+accessFileId);
                principal=rs.getString(2);
                //System.out.println("principal: "+principal);
                permission=rs.getInt(3);
                //System.out.println("permission: "+permission);
                permType=rs.getString(4);
                //System.out.println("permType: "+permType);
                permOrder=rs.getString(5);
                //System.out.println("permOrder: "+permOrder);
                beginTime=rs.getString(6);
                //System.out.println("beginTime: "+beginTime);
                endTime=rs.getString(7);
                //System.out.println("endTime: "+endTime);
                ticketCount=rs.getInt(8);
                //System.out.println("ticketCount: "+ticketCount);
            
                insertStatement = conn.prepareStatement(
                  "INSERT INTO xml_access " + 
                  "(docid, principal_name, permission, perm_type, perm_order,"+
                  "ticket_count, accessfileid) VALUES "+
                  "(?,?,?,?,?,?,?)");
                // Bind the values to the query
                insertStatement.setString(1, docId);
                insertStatement.setString(2, principal);
                insertStatement.setInt(3, permission);
                insertStatement.setString(4, permType);
                insertStatement.setString(5, permOrder);
                insertStatement.setString(7, accessFileId);
                if ( ticketCount > 0 ) 
                { 
                  insertStatement.setString(6, "" + ticketCount);
                } 
                else 
                {
                  insertStatement.setString(6, null);
                }
                logMetacat.debug("running sql: " + insertStatement.toString());
                insertStatement.execute();
                hasRecord=rs.next();
              }//while
              insertStatement.close();
            }//else
            
          }//while
          pStmt.close();
        }//try
        catch (SQLException e)
        {
          pStmt.close();
          insertStatement.close();
          logMetacat.error("Error in getDocidListForDataPackadge: "
                            +e.getMessage());
        }//catch
        
      }//else
      
  }//AccociateAccess
 
}//class