/* $Id: HopDeleteDeps.java 988245 2010-08-23 18:39:35Z kwright $ */

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.manifoldcf.crawler.jobs;

import org.apache.manifoldcf.core.interfaces.*;
import org.apache.manifoldcf.crawler.interfaces.*;
import org.apache.manifoldcf.crawler.system.Logging;
import org.apache.manifoldcf.crawler.system.ManifoldCF;
import java.util.*;

/** This class manages the table that keeps track of link deletion dependencies for cached
* hopcounts.
* 
* <br><br>
* <b>hopdeletedeps</b>
* <table border="1" cellpadding="3" cellspacing="0" summary="">
* <tr class="TableHeadingColor">
* <th>Field</th><th>Type</th><th>Description&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
* <tr><td>jobid</td><td>BIGINT</td><td>Reference:jobs.id</td></tr>
* <tr><td>ownerid</td><td>BIGINT</td><td>Reference:hopcount.id</td></tr>
* <tr><td>linktype</td><td>VARCHAR(255)</td><td></td></tr>
* <tr><td>parentidhash</td><td>VARCHAR(40)</td><td></td></tr>
* <tr><td>childidhash</td><td>VARCHAR(40)</td><td></td></tr>
* </table>
* <br><br>
* 
*/
public class HopDeleteDeps extends org.apache.manifoldcf.core.database.BaseTable
{
  public static final String _rcsid = "@(#)$Id: HopDeleteDeps.java 988245 2010-08-23 18:39:35Z kwright $";

  // Field names
  public static final String jobIDField = "jobid";
  public static final String ownerIDField = "ownerid";
  public static final String linkTypeField = "linktype";
  public static final String parentIDHashField = "parentidhash";
  public static final String childIDHashField = "childidhash";

  /** Constructor.
  *@param database is the database handle.
  */
  public HopDeleteDeps(IDBInterface database)
    throws ManifoldCFException
  {
    super(database,"hopdeletedeps");
  }

  /** Install or upgrade.
  */
  public void install(String jobsTable, String jobsColumn, String hopCountTable, String idColumn)
    throws ManifoldCFException
  {
    // Standard practice: outer retry loop
    while (true)
    {
      Map existing = getTableSchema(null,null);
      if (existing == null)
      {
        HashMap map = new HashMap();
        map.put(jobIDField,new ColumnDescription("BIGINT",false,false,jobsTable,jobsColumn,false));
        map.put(ownerIDField,new ColumnDescription("BIGINT",false,false,hopCountTable,idColumn,false));
        map.put(linkTypeField,new ColumnDescription("VARCHAR(255)",false,true,null,null,false));
        map.put(parentIDHashField,new ColumnDescription("VARCHAR(40)",false,false,null,null,false));
        map.put(childIDHashField,new ColumnDescription("VARCHAR(40)",false,true,null,null,false));

        performCreate(map,null);
      }
      else
      {
        // Upgrade code goes here, if needed.
      }

      // Index management
      IndexDescription completeIndex = new IndexDescription(true,new String[]{ownerIDField,parentIDHashField,linkTypeField,childIDHashField});
      IndexDescription jobChildIndex = new IndexDescription(false,new String[]{jobIDField,childIDHashField});

      // Get rid of indexes that shouldn't be there
      Map indexes = getTableIndexes(null,null);
      Iterator iter = indexes.keySet().iterator();
      while (iter.hasNext())
      {
        String indexName = (String)iter.next();
        IndexDescription id = (IndexDescription)indexes.get(indexName);

        if (completeIndex != null && id.equals(completeIndex))
          completeIndex = null;
        else if (jobChildIndex != null && id.equals(jobChildIndex))
          jobChildIndex = null;
        else if (indexName.indexOf("_pkey") == -1)
          // This index shouldn't be here; drop it
          performRemoveIndex(indexName);
      }

      // Add the ones we didn't find
      if (completeIndex != null)
        performAddIndex(null,completeIndex);
      if (jobChildIndex != null)
        performAddIndex(null,jobChildIndex);

      break;
    }
  }

  /** Uninstall.
  */
  public void deinstall()
    throws ManifoldCFException
  {
    performDrop(null);
  }

  /** Analyze job tables that need analysis.
  */
  public void analyzeTables()
    throws ManifoldCFException
  {
    long startTime = System.currentTimeMillis();
    Logging.perf.debug("Beginning to analyze hopdeletedeps table");
    analyzeTable();
    Logging.perf.debug("Done analyzing hopdeletedeps table in "+new Long(System.currentTimeMillis()-startTime)+" ms");
  }

  /** Delete a job. */
  public void deleteJob(Long jobID)
    throws ManifoldCFException
  {
    ArrayList list = new ArrayList();
    String query = buildConjunctionClause(list,new ClauseDescription[]{
      new UnitaryClause(jobIDField,jobID)});
    performDelete("WHERE "+query,list,null);
    // Log one event - it may not be enough, but it's the best we can do without overhead
    noteModifications(0,0,1);
  }

  /** Remove rows that correspond to specific hopcount records.
  */
  public void removeMarkedRows(String parentTable, String parentIDHashField, String query, ArrayList queryList)
    throws ManifoldCFException
  {
    // This didn't perform very well.
    //performDelete("WHERE EXISTS(SELECT 'x' FROM "+parentTable+" t0 WHERE t0."+parentIDField+"="+ownerIDField+
    //      " AND t0."+markField+"=?)",list,null);
    performDelete("WHERE "+ownerIDField+" IN(SELECT "+parentIDHashField+" FROM "+parentTable+" WHERE "+query+")",
      queryList,null);
    // Log one event - it may not be enough, but it's the best we can do without overhead
    noteModifications(0,0,1);
  }

  /** Delete rows related to specified owners.  The list of
  * specified owners does not exceed the maximum database in-clause
  * size.
  */
  public void deleteOwnerRows(Long[] ownerIDs)
    throws ManifoldCFException
  {
    ArrayList list = new ArrayList();
    
    String query = buildConjunctionClause(list,new ClauseDescription[]{
      new MultiClause(ownerIDField,ownerIDs)});
      
    performDelete("WHERE "+query,list,null);
    noteModifications(0,0,ownerIDs.length);
  }

  /** Get the delete dependencies for an owner.
  *@return the links
  */
  public DeleteDependency[] getDeleteDependencies(Long ownerID)
    throws ManifoldCFException
  {
    ArrayList list = new ArrayList();
    String query = buildConjunctionClause(list,new ClauseDescription[]{
      new UnitaryClause(ownerIDField,ownerID)});
      
    IResultSet set = performQuery("SELECT "+linkTypeField+", "+parentIDHashField+", "+
      childIDHashField+" FROM "+getTableName()+" WHERE "+query,list,null,null);
    DeleteDependency[] rval = new DeleteDependency[set.getRowCount()];
    int i = 0;
    while (i < rval.length)
    {
      IResultRow row = set.getRow(i);
      rval[i] = new DeleteDependency((String)row.getValue(linkTypeField),
        (String)row.getValue(parentIDHashField),
        (String)row.getValue(childIDHashField));
      i++;
    }
    return rval;
  }

  /** Delete a dependency */
  public void deleteDependency(Long ownerID, DeleteDependency dd)
    throws ManifoldCFException
  {
    ArrayList list = new ArrayList();
    String query = buildConjunctionClause(list,new ClauseDescription[]{
      new UnitaryClause(ownerIDField,ownerID),
      new UnitaryClause(parentIDHashField,dd.getParentIDHash()),
      new UnitaryClause(linkTypeField,dd.getLinkType()),
      new UnitaryClause(childIDHashField,dd.getChildIDHash())});
        
    performDelete("WHERE "+query,list,null);
    noteModifications(0,0,1);
  }

  /** Write a delete dependency.
  */
  public void writeDependency(Long ownerID, Long jobID, DeleteDependency dd)
    throws ManifoldCFException
  {
    if (Logging.hopcount.isDebugEnabled()) {
      Logging.hopcount.debug("Writing hopcount dependency for job "+jobID+" owner "+ownerID+" linktype='"+dd.getLinkType()+"' parenthash='"+dd.getParentIDHash()+" childhash='"+dd.getChildIDHash()+"'");
    }
    HashMap map = new HashMap();
    map.put(jobIDField,jobID);
    map.put(ownerIDField,ownerID);
    map.put(linkTypeField,dd.getLinkType());
    map.put(parentIDHashField,dd.getParentIDHash());
    map.put(childIDHashField,dd.getChildIDHash());
    performInsert(map,null);
    noteModifications(1,0,0);
  }

}
