// SQLRetrieverThread class
// Programmed by Alan Maxwell
//
// Revision History
// ----------------
// v1.21: 13 Dec 2002
//   Added an option 'doRowTotal' which specifies whether the result page should
//   show the total number of rows the query returned when only a subset is
//   displayed in summary... access via get/set methods...
// v1.20: 28 Nov 2002
//   Renamed the class from SQLRetriever to SQLRetrieverThread!
//   Rewrite the class to implement the 'runnable' interface, allowing this
//   class to run as a separate thread. This will allow the SQL retrieval
//   to proceed in the background while the user/gui gets on with other
//   things.
//   The controlling class can run in a while loop and test for
//   'isQueryFinished()' periodically. The query can ended prematurely by
//   calling 'stopQuery()'.
// v1.05: 30 Oct 2002
//   Worked on a 'formatter' array which takes an array of SQLDataFormatter
//   objects for each of the columns describing how to format them properly.
//   This 'formatting' array is built up ONCE (as the heading row is formed)
//   with a call to getSQLDataFormatterArray(). Then, as we iterate through the
//   returned rows, we can refer to the format 'lookup table' and return the
//   nicely formatted data...
//
// v1.02: 25 Oct 2002
//   Removed the CSVSafeString() function which checks that strings have no
//   commas or quotes in them - Mike will make sure that the SQL database
//   does not have these in the data...
//
// v1.01: 15 Oct 2002
//   Revised the constructor to accept the new SQLServerInfoJDBC class which
//   more tightly and neatly encompasses all the info associated with a JDBC
//   SQLServer
//
// v1.00: 03 Oct 2002
//   Initial Release
//
// Synopsis
// --------
// This class allows the user to specify details of an MS SQL Server database
// and then execute SQL queries on that database (assuming a successful
// connection can be established) to return the results as an neat HTML
// document, with the query results returned as a neatly formatted table.
//
// The user can apply special formatting to specific data columns by
// implementing a subclass of this object, and overriding the formatColumnData()
// method to return the desired (string) representation of the data...

package net.alanmaxwell.sql;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.util.StringTokenizer;

//import FormatRS.*;
import java.sql.*;

import javax.servlet.http.*;
import net.mar.FormatRS;
import net.mar.ParseVOT;
import net.mar.FITSWriter;
import net.alanmaxwell.html.HTMLStrings;

import net.alanmaxwell.sql.SQLServerInfoJDBC;
import net.alanmaxwell.sql.SQLDataFormatter;

import javax.mail.*;
import javax.mail.internet.*;
/**
 * After providing the correct initialisation, the run() method call be called
 * either specifically or as a separate thread to have the SQL query executed.
 * The value of isQueryFinished() can be repeatedly tested to see if the query
 * has completed before calling getResultsHTML() to return the HTML result
 * of the query (usually a results table with pre/post amble but sometimes an
 * error message).
 *
 * @author Alan Maxwell
 */
public class SixSQLRetrieverThread_DR3 implements Runnable {
  // Some constants...
  public static final int SQLROWS_DEFAULT = 50; // Default returned # table rows
  public static final int SQLROWS_UPPERLIMIT = 10000; // Maximum EVER returned rows..
  public static final int ROWSWRITTEN_UPPERLIMIT = 15000000;
  public static final int FACTOR=2; // total rows cycle through = factor * rowswriitenupperlimit

  public static final int OUTPUT_NONE = 0;
  public static final int OUTPUT_CSV = 1;
  public static final int OUTPUT_FITS = 2;
  public static final int OUTPUT_VOT = 3;
  public static final int COMPRESS_NONE = 0;
  public static final int COMPRESS_ZIP = 1;
  public static final int COMPRESS_GZIP = 2;

  public String uploadFile=null;
  public BufferedReader uploadBufferedReader;

  public static final int OUTPUT_BUFFERSIZE = 524288; // .5Meg file buffer!

  // Important vars that are accessed by several functions, not synchronized!
  protected Connection databaseConnection = null;
  protected Statement  databaseQuery = null;
  protected ResultSet  results = null;
  protected ResultSetMetaData resultsMetaData = null;

  protected int row = 0;
  protected String uniqID=null;
  protected int rowsTotalUpperLimit=0;
  protected int rowsWrittenUpperLimit = 0;
  protected int rowsWrittenToFile = 0;
  protected int resultsNumColumns = 0;

  protected String columnData = "";
  protected String [] columnNames = null;
  protected SQLDataFormatter [] columnFormats = null;
  FormatRS frs= new FormatRS();
  String CSVName = "";
  String CSVFileName = "";
  String FITSName ="";
  String FITSFileName = "";
  String emailAddress=null;

  // 'static' initialiser is called when class is first loaded to initialise
  // and install the database driver(s)!
  static {
    // Load in the Microsoft-provided JDBC SQL Server driver
    try
    {
      Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
    }
    catch (ClassNotFoundException e)
    {
      // A class not found error means the SQL driver class could not be found.
      // Which means that this driver would not be available until this is
      // remedied. This flag variable will signal this to the class!

      System.err.println(
        "SQLRetrieverThread::static(): Could not load SQLServerDriver class!"
      );
    };
  }; // END of 'static' initialiser

  // The CSVNumber variable is used to generate unique filenames for CSV files
  // when people request them. The idea when CSV is made, the getNextCSVNumber()
  // method will be called to return the next number in sequence and also to
  // increment the counter. Note that thisd is a 'synchronised' method, meaning
  // that it is THREAD SAFE and so can be called by any thread (as long as it is
  // through the getNextCSVNumber() method. DO NOT MODIFY CSVNumber variable DIRECTLY!!
  private static final int CVSNUM_UPPERLIMIT = 60000;
  private static int CSVNumber = 0; // 'Unique' counter for CSV requests/filenames

  private static synchronized int getNextCSVNumber()
  {
    CSVNumber++;

    if (CSVNumber > CVSNUM_UPPERLIMIT)
    {
      CSVNumber = 0;
    };

    return CSVNumber;
  };

  public SixSQLRetrieverThread_DR3(SQLServerInfoJDBC newServerInfo) {
    setServerInfo(newServerInfo);
  };


  protected SQLServerInfoJDBC serverInfo = null;

  protected synchronized void setServerInfo(SQLServerInfoJDBC newServerInfo) {
    serverInfo = newServerInfo;
  };



  	public synchronized void setUploadFile(String newUploadFile)
  	{
		 uploadFile=newUploadFile;
  }
  	String user="";
    public void setUser(String user) {
        this.user=user;
    }

  	public synchronized void setUpBufferedReader(BufferedReader newUpBufferedReader)
  	{
		BufferedReader uploadBufferedReader=newUpBufferedReader;
	/*	        String line;
		        try {
					System.out.println("heeeeere" +"<br>");
		       while ((line=upBufferedReader.readLine()) !=null){

		System.out.println(line +"<br>");
		}
	}
	catch(IOException e) {
	}*/

  }
  protected boolean queryTerminated = false;

  public synchronized void setQueryTerminated(boolean status) {
    queryTerminated = status;
  };

  public synchronized boolean isQueryTerminated() {
    return queryTerminated;
  };



  protected boolean queryProcessing = true;

  public synchronized void setQueryProcessing(boolean status) {
    queryProcessing = status;
  };

  public synchronized boolean isQueryProcessing() {
    return queryProcessing;
  };

  boolean mightBeBackground=false;
  public synchronized void setMightBeBackground(boolean newMBB) {
    mightBeBackground=newMBB;
  };

  public synchronized boolean isMightBeBackground() {
    return mightBeBackground;
  };



public synchronized void setEmailAddress(String newEmailAddress) {
	  emailAddress=newEmailAddress;
	    }
public synchronized String getEmailAddress() {
	  return emailAddress;
	    }

  protected String radius = "0";
  public synchronized void setRadius(String newRadius) {
  radius=newRadius;
    }

  protected String pair = "nearest";
  public synchronized void setPair(String newPair) {
  pair=newPair;
	}


  protected int rowsHTML = SQLROWS_DEFAULT;

  public synchronized void setRowsHTML(int newRows) {
    if (newRows > SQLROWS_UPPERLIMIT)
    {
      rowsHTML = SQLROWS_UPPERLIMIT;
    }
    else if (newRows < 0)
    {
      rowsHTML = 0;
    }
    else
    {
      rowsHTML = newRows;
    };
  };

  public synchronized int getRowsHTML() {
    return rowsHTML;
  };

  public synchronized int getRowsReturned() {
    return row;
  };

  protected boolean doRowTotal = true;

  public synchronized boolean getDoRowTotal() {
    return doRowTotal;
  };

  public synchronized void setDoRowTotal(boolean status) {
    doRowTotal = status;
  };

  protected String SQLQuery = null;

  public synchronized void setSQLQuery(String newQuery) {
    SQLQuery = newQuery;

    setQueryProcessing(true); // We haven't got the results for this query...
  };

  public synchronized String getSQLQuery() {
    return SQLQuery;
  };


  protected int dataOutputFormat = OUTPUT_NONE;

  public synchronized void setOutputFormat(int newFormat) {
    dataOutputFormat = newFormat;
  };

  public synchronized int getOutputFormat() {
    return dataOutputFormat;
  };


  protected int dataOutputCompression = COMPRESS_NONE;

  public synchronized void setOutputCompression(int newCompression) {
    dataOutputCompression = newCompression;
  };

  public synchronized int getOutputCompression() {
    return dataOutputCompression;
  };


  protected StringBuffer htmlResults = new StringBuffer("");

  protected synchronized void clearResultsHTML() {
    htmlResults = new StringBuffer("");
  };

  protected synchronized void addToResultsHTML(String newLine) {
    htmlResults.append(newLine);
  };

  public synchronized String getResultsHTML() {
    return htmlResults.toString();
  };


  protected String HTMLTableRowColorHeader = "#FFFFCC";
  //protected String HTMLTableRowColorOdd = "#99CCFF";
  //protected String HTMLTableRowColorOdd = "#FFCCCC";
  protected String HTMLTableRowColorOdd = "#66CCFF";
  protected String HTMLTableRowColorEven = "#CCCCCC";

  public synchronized void setHTMLTableColorRowHeader(String newColor) {
    HTMLTableRowColorHeader = newColor;
  };

  public synchronized String getHTMLTableColorRowHeader() {
    return HTMLTableRowColorHeader;
  };

  public synchronized void setHTMLTableColorRowOdd(String newColor) {
    HTMLTableRowColorOdd = newColor;
  };

  public synchronized String getHTMLTableColorRowOdd() {
    return HTMLTableRowColorOdd;
  };

  public synchronized void setHTMLTableColorRowEven(String newColor) {
    HTMLTableRowColorEven = newColor;
  };

  public synchronized String getHTMLTableColorRowEven() {
    return HTMLTableRowColorEven;
  };

  protected String outputBaseDIR = "";

  public synchronized void setOutputBaseDIR(String newDir) {
    outputBaseDIR = newDir;
  };

  public synchronized String getOutputBaseDIR() {
    return outputBaseDIR;
  };


  protected String outputBaseURL = "";

  public synchronized void setOutputBaseURL(String newUrl) {
    outputBaseURL = newUrl;
  };

  public synchronized String getOutputBaseURL() {
    return outputBaseURL;
  };


  protected String outputAppendage = "";

  public synchronized void setOutputAppendage(String newAppendage) {
    outputAppendage = newAppendage;
  };

  public synchronized String getOutputAppendage() {
    return outputAppendage;
  };


  protected void generateErrorHTML(String errorMessage) {
    clearResultsHTML();
    addToResultsHTML(
      HTMLStrings.getHTMLErrorFragment(errorMessage)
    );
  };

  // The following functions generate 'formatter arrays' which hold an array
  // of SQLDataFormatter (or subclasses) objects that apply formatting to the
  // related data cell...








  protected SQLDataFormatter getSQLDataFormatterForColumn(int column)
  {
	  // new six
	   String colName = "";
	      String colType = "";

	      // Try to determine the column name and format it differently depending...
	      try
	      {
	      	colName = resultsMetaData.getColumnName(column);
	      }
	      catch (SQLException e)
	      {
	        colName = "";
	      };

	      if
	        (
	          (colName.equalsIgnoreCase("RA")) ||
	          (colName.equalsIgnoreCase("OBSRA")) ||
	          (colName.equalsIgnoreCase("UP_RA"))
	        )
	      {
	        if (getShowHMS())
	        {
	          return new SQLDataFormatterSexagecimal(true, 2);
	        }
	        else
	        {
	          return new SQLDataFormatter();
	        }
	      }
	      else if
	        (
	          (colName.equalsIgnoreCase("DEC")) ||
	          (colName.equalsIgnoreCase("OBSDEC")) ||
	          (colName.equalsIgnoreCase("UP_DEC"))

	        )
	      {
	        if (getShowHMS())
	        {
	          return new SQLDataFormatterSexagecimal(false, 1);
	        }
	        else
	        {
	          return new SQLDataFormatter();
	        }
   		 }
// end new six
    return new SQLDataFormatter();
  };

  private SQLDataFormatter[] getSQLDataFormatterArray()
  {
    // Can access MaxColumns, which should represent the num columns in result!
    // Can access 'metadata' object, which should have been set before now!

    // BE AWARE OF THE DIFFERENCE IN ARRAY NUMBERING, SQL columns start at 1,
    // Java arrays start at 0!!!

    SQLDataFormatter[] tempArray = new SQLDataFormatter[resultsNumColumns];

    for (int column=1; column <= resultsNumColumns ; column++ )
    {
      tempArray[column - 1] = getSQLDataFormatterForColumn(column);
    };

    return tempArray;
  };

 /* protected String getPreTableHTML()
  {
    return "";
  };*/


    protected String getPreTableHTML()
    {
      return "";
      /*
        "    <p> \n" +
        "      <small> \n" +
        "        NOTE: If your results contain the columns 'targetname' or 'specid' \n" +
        "        you will see 'Show' appear as a link towards the left edge of the table. \n" +
        "        This link opens a new browser window and displays any related object \n" +
        "        images and/or spectra - subsequently displayed images/spectra should appear \n" +
        "        in the same window.\n" +
        "      </small> \n" +
        "    </p>";
        */
  };



/*
  protected void verifyQueryAndOptions() throws Exception {
    // Dummy class does nothing... override in subclass if needed...
  };

 */

  protected void verifyQueryAndOptions() throws Exception
    {
      if (getLinkTAR() == true)
      {
        boolean searchingTargetName = true;

        int column = 1;
        while (
                (searchingTargetName) &&
                (column <= resultsNumColumns)
              )
        {
          if (columnNames[column-1].equalsIgnoreCase("TARGETNAME"))
          {
            searchingTargetName = false;
          }

          column++;
        }

        if (searchingTargetName)
        {
          throw new Exception(
            "A TAR file was requested with the 'tar=yes' option " +
            "but the query results do not contain the column 'targetname' so " +
            "the request cannot be completed. Please include the 'targetname' " +
            "column from any table if you wish to obtain a TAR file."
          );
        }
      };
  };


  protected String getPostTableHTML()
  {
StringBuffer tempStringBuffer = new StringBuffer("");

	     if (getLinkTAR() == true)
	      {
	        //StringBuffer tempStringBuffer = new StringBuffer("");

	        tempStringBuffer.append(
	          "    <p> \n" +
	          "      <img src=\"" + getOutputBaseURL() + "images/icons/tar.gif\" " +
	                     "alt=\"Tar icon\" /> \n" +
	          "      <a href=\""+SIXDFSchema.BASECGIURL+"tar.cgi?release=dr3&file=" +getOutputAppendage()+ CSVFileName +
	                   "&email=" + getLinkEMAIL() + "\" >" +
	                   "Request a TAR archive</a>" +
	          "      of all available FITS files relating to objects returned by your query. \n" +
	          "      An e-mail will be sent to <i>" + getLinkEMAIL() + "</i> once your TAR file is ready. \n " +
	  //        "      <br /> \n" +
	          "      (Estimated size of the TAR is "
	        );

	        double TARFileEstimate = row * 200000.0;

	        if (TARFileEstimate > 1000000)
	        {
	          tempStringBuffer.append(
	            HTMLStrings.numbersTo1DP.format(TARFileEstimate/1024.0/1024.0) +
	            " MB). \n"
	          );
	        }
	        else if (TARFileEstimate > 1000)
	        {
	          tempStringBuffer.append(
	            HTMLStrings.numbersTo1DP.format(TARFileEstimate/1024.0) +
	            " KB). \n"
	          );
	        }
	        else
	        {
	          tempStringBuffer.append(TARFileEstimate + " bytes). \n");
	        };


	         tempStringBuffer.append(
	          "    </p> \n\n" +
	          "    <p> \n" +
	          "      NOTE: Please do not request TAR files when your CSV file \n" +
	          "      contains more than 1000 rows <br /> \n" +
	          "      (this is due to the probable large size of the resultant TAR file). \n" +
	          "    </p> \n\n"
	        );
}




    String firstPart, secondPart = "";
    String extra="";
    if (row >= rowsTotalUpperLimit)
    {
		extra=" <b> at least (i.e. before it was halted) </b> ";
	}

    if (getDoRowTotal())
    {
      if (row == 1)
      {
        firstPart =
          "      (Query returned 1 result row, \n ";
      }
      else
      {
        firstPart =
          "      (Query returned " + extra + row + " result rows, \n";
      }

      if (rowsHTML == 0)
      {
        secondPart =
          "      no rows are shown in the displayed table.) \n";
      }
      else if (row <= rowsHTML)
      {
        secondPart =
          "      all rows are shown in the displayed table.) \n";
      }
      else if (rowsHTML == 1)
      {
        secondPart =
          "      only the first row is shown in the displayed table.) \n";
      }
      else
      {
        secondPart =
          "      only the first " + rowsHTML + " rows are shown in the displayed table.) \n";
      }
    }
    else
    {
      if (row <= rowsHTML)
      {
        if (row == 1)
        {
          firstPart =
            "      (Query returned 1 result row, \n";
        }
        else
        {
          firstPart =
            "      (Query returned " + row + " result rows, \n";
        }

        secondPart =
          "      all rows are shown in the displayed table.) \n";
      }
      else if (rowsHTML == 0)
      {
        firstPart =
          "      (Query returned more than 0 result rows, \n";
        secondPart =
          "      no rows are shown in the displayed table.) \n";
      }
      else if (rowsHTML == 1)
      {
        firstPart =
          "      (Query returned more than 1 result row, \n";
        secondPart =
          "      only the first row is shown in the displayed table.) \n";
      }
      else
      {
        firstPart =
          "      (Query returned more than " + rowsHTML + " result rows, \n";
        secondPart =
          "      only the first " + rowsHTML + " rows are shown in the displayed table.) \n";
      }
    }

    return
    tempStringBuffer.toString()+
      "    <p> \n" +
      firstPart +
      secondPart +
      "    </p> \n\n";
  };


protected String getInsertedTableCellsHeaderHTML()
  {
    // Reset these for the new query...
    specIdColumn = 0;
    targetNameColumn = 0;
    targetIdColumn = 0;
    showClassColumn=0;
    obsDecColumn=0;
    obsRAColumn=0;
    return
      "        <th>&nbsp;</th> \n" +
      "        <th>&nbsp;</th> \n";
  };

  protected String getInsertedTableCellsDataHTML()
  {
    StringBuffer resultString = new StringBuffer("");
    String specid = "-1";
    String targetname = "-1";
    String targetid = "-1";
    String showClass = "-1";
    
    // See if 'TARGETNAME' and/or 'SPECID' are in the returned results...

    // If we've not already discovered the columns we're after...
    if (targetNameColumn == 0 && showClassColumn==0) // Any one of the three would do here...
    {
      targetNameColumn = -1; // Assume it can't be found unless below...
      specIdColumn = -1;
      targetIdColumn = -1;
      showClassColumn = -1;
      obsRAColumn=-1;
      obsDecColumn=-1;
      for (int column = 1; column <= resultsNumColumns ; column++)
      {
        if ( (targetNameColumn < 0) &&
             (columnNames[column - 1].equalsIgnoreCase("TARGETNAME")) )
        {
          targetNameColumn = column;
        };
        if ( (showClassColumn < 0) &&
                (columnNames[column - 1].equalsIgnoreCase("showClass")) )
           {
             showClassColumn = column;
           };
           if ( (obsRAColumn < 0) &&
                   (columnNames[column - 1].equalsIgnoreCase("obsRA")) )
              {
                obsRAColumn = column;
              };
              if ( (obsDecColumn < 0) &&
                      (columnNames[column - 1].equalsIgnoreCase("obsDec")) )
                 {
                   obsDecColumn = column;
                 };
                 

        if ( (specIdColumn < 0) &&
             (columnNames[column - 1].equalsIgnoreCase("SPECID")) )
        {
          specIdColumn = column;
        };

        if ( (targetIdColumn < 0) &&
             (columnNames[column - 1].equalsIgnoreCase("TARGETID")) )
        {
          targetIdColumn = column;
        };
      };
    };

    if (targetNameColumn > 0)
    {
      try
      {
      	targetname = results.getString(targetNameColumn);
      }
      catch (SQLException se)
      {
        targetname = "-1";
      }
    }
    if (showClassColumn > 0)
    {
      try
      {
      	targetname = results.getString(showClassColumn);
      }
      catch (SQLException se)
      {
        targetname = "-1";
      }
    }
    
    if (specIdColumn > 0)
    {
      try
      {
      	specid = results.getString(specIdColumn);
      }
      catch (SQLException se)
      {
        specid = "-1";
      }
    }

    if (targetIdColumn > 0)
    {
      try
      {
      	targetid = results.getString(targetIdColumn);
      }
      catch (SQLException se)
      {
        targetid = "-1";
      }
    }

    resultString.append(
      "        <td nowrap=\"nowrap\" class=\"RowHeader\" bgcolor=\"" +
      getHTMLTableColorRowHeader() + "\"><b>" + row + "</b></td> \n"
    );

    if ((targetNameColumn > 0) || (showClassColumn >0) || (specIdColumn > 0))
    {
      resultString.append(
        "        <td nowrap=\"nowrap\" class=\"RowHeader\" bgcolor=\"" +
        getHTMLTableColorRowHeader() + "\"> \n" +
        //"<input type=\"hidden\" name=\"show\" value=\""+"name=" + targetname +
        "<input TYPE=\"checkbox\" name=\"show\" value=\""+"name=" + targetname +
        "&tid=" + targetid +
        "&specid=" + specid +"\">"+
        "          <a href=\"" + SIXDFSchema.BASECGIURL);// + "show");
        
        if (showClassColumn > 0) {
            resultString.append("private/showClass"); 
        }
        else {
            resultString.append("show");
        }
        resultString.append(".cgi?release=dr3&" +
        "targetname=" + targetname +
        "&tid=" + targetid +
        "&specid=" + specid);
        if (showClassColumn > 0) {
            resultString.append("&row="+row+"&uniqID="+uniqID+"&name="+user); 
        }
        try {
        if (obsRAColumn > 0 && obsDecColumn >0 ) {
            resultString.append("&ra="+results.getString(obsRAColumn)+"&dec="+results.getString(obsDecColumn));
        }
        }
        catch (SQLException se) {
            
        }
        resultString.append("\" target=\"Show\">Show</a> \n" +
        "        </td> \n"
      );
    }
    else
    {
      resultString.append(
        "        <td nowrap=\"nowrap\" class=\"RowHeader\" bgcolor=\"" +
        getHTMLTableColorRowHeader() + "\">&nbsp;</td> \n"
      );
    }

    return resultString.toString();
  };

/*  protected String getInsertedTableCellsHeaderHTML()
  {
    return
      "        <th>&nbsp;</th> \n";
  };

  protected String getInsertedTableCellsDataHTML()
  {
    return
      "        <td nowrap=\"nowrap\" class=\"RowHeader\" bgcolor=\"" +
      getHTMLTableColorRowHeader() +
      "\"><b>" + row + "</b></td> \n";
  };*/


  // new six
  private int specIdColumn = 0; // 0 = not searched; -1 = can't find, n = column
  private int targetNameColumn = 0; // 0 = not searched; -1 = can't find, n = column
  private int targetIdColumn = 0; // 0 = not searched; -1 = can't find, n = column
  private int showClassColumn = 0;
  private int obsRAColumn = 0;
  private int obsDecColumn = 0;
  
  protected boolean showHMS = true; // Should we use HMS for RA/DEC?

  public synchronized void setShowHMS(boolean status) {
    showHMS = status;
  };

  public synchronized boolean getShowHMS() {
    return showHMS;
  };


  protected boolean linkTAR = false;

  public synchronized void setLinkTAR(boolean status) {
    linkTAR = status;
  };

  public synchronized boolean getLinkTAR() {
    return linkTAR;
  };


  protected String linkEMAIL = "";

  public synchronized void setLinkEMAIL(String newEmail) {
    linkEMAIL = newEmail;
  };

  public synchronized String getLinkEMAIL() {
    return linkEMAIL;
  };
 // end six new




  /**
   * Execute the SQL query on the provided JDBC server and store the results
   * in the htmlResults StringBuffer ready for later retrieval...
   *
   * @author Alan Maxwell
   */
  public void run() {
    setQueryProcessing(true);
    setQueryTerminated(false);

    StringBuffer resultsStringBuffer = new StringBuffer("");


    int runningNumber = getNextCSVNumber();


    uniqID = String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_MONTH))+"_"+
    				String.valueOf(Calendar.getInstance().get(Calendar.HOUR_OF_DAY))+"_"+
    				String.valueOf(Calendar.getInstance().get(Calendar.MINUTE))+"_"+
    				String.valueOf(Calendar.getInstance().get(Calendar.SECOND))+
    				"_"+runningNumber;



    PrintWriter  CSVWriter = null;
    OutputStream CSVOutputStream = null;

    // Cache some variables to avoid repeated thread-locked access (slow)
    int OutputFormatCache = getOutputFormat();

    try
    {
      // System.out.println ("1a");
      try
      {
        // Main execution here - retrieving of query and formatting results...
        if (serverInfo == null)
        {
          generateErrorHTML("<b>Error in run():</b> No serverInfo provided.");
          return;
        };





        // Open database connection...
        databaseConnection =
          DriverManager.getConnection(serverInfo.databaseURL,
                                      serverInfo.dbUsername,
                                      serverInfo.dbPassword);


        // Attempt to 'do' the query...

        if (uploadFile == null){
        databaseQuery =
          databaseConnection.createStatement();
databaseQuery.setQueryTimeout(7200);
        results = databaseQuery.executeQuery(SQLQuery);
     //   System.out.println ("1");

	}
	else {

		  databaseQuery =
		          databaseConnection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
          //databaseQuery.setQueryTimeout(7200);
		StringBuffer SQLQueryBuffer = null;
	//	System.out.println("h1");
		try {

        databaseQuery.executeUpdate("create table #upload (up_ra float, up_dec float, up_id int, up_name varchar(16)) ");
 //       SQLQueryBuffer= new StringBuffer("create table #upload (up_ra float, up_dec float, up_id int, up_name varchar(16)) ");


		File file=new File(uploadFile);
        FileReader fr = new FileReader(file);
        BufferedReader in = new BufferedReader(fr);
        String line;
/*       while ((line=uploadBufferedReader.readLine()) !=null){

resultsStringBuffer.append(line +"<br>");
		}*/
        int count=0;

        while ( ((line=in.readLine()) !=null) && !isQueryTerminated() ){
			count=count+1;
			String lineTrim=line.trim();
            int noParts=0;
			StringTokenizer st = new StringTokenizer(lineTrim," ,");
			noParts=st.countTokens();
	//	    System.out.println(count);
			String[] parts = new String[st.countTokens()];

			for ( int i = 0; i < noParts; i++ ) {
			    parts[ i ] = st.nextToken();
//			    resultsStringBuffer.append(parts[i] + ":" + i +"<br>");
			}
			if (noParts > 2){
/*			SQLQueryBuffer.append(" insert into #upload (up_ra, up_dec, up_id, up_name) values ("+
			parts[1] +","+parts[2]+","+count+",'"+parts[0]+"'	)");*/
			//	System.out.println("h2");
    databaseQuery.executeUpdate("insert into #upload  values ("+
    parts[1] +","+parts[2]+","+count+",'"+parts[0]+"'	)");
//System.out.println("h3");
		}


		}

		//SQLQueryBuffer.append(" create table #x (up_id int,objID bigint,distance float)");
       databaseQuery.executeUpdate("create table #x (up_id int,objID bigint,distance float)");

	    /*if (pair.equals("nearest")){
		SQLQueryBuffer.append(" INSERT INTO #x SELECT up_id, dbo.fGetNearestObjIdEqSSA(up_ra,up_dec,"+radius+",1) as objId,dbo.fGetNearestObjDistEqSSA(up_ra,up_dec,+"+radius+",1) as distance");
		SQLQueryBuffer.append(" FROM #upload WHERE dbo.fGetNearestObjidEqSSA(up_ra,up_dec," + radius + ",1) IS NOT NULL");
		}
		else {
			SQLQueryBuffer.append("insert into #x EXEC dbo.spGetNeighborsSSA " + radius +", 3 ");
		}
		SQLQueryBuffer.append(" select up_name,up_ra,up_dec,#upload.up_id,distance,"+ SQLQuery +" from #upload left outer join #x on #upload.up_id = #x.up_id left outer join"+
		" source on #x.objid=source.objid order by #upload.up_id,distance");*/

		//SQLQueryBuffer.append(SQLQuery);


		file.delete();
	}
		catch (FileNotFoundException e){
		}
//		databaseQuery.setQueryTimeout(3);
//		resultsStringBuffer.append("timeout set");
		results = databaseQuery.executeQuery(SQLQuery);
	}

        resultsMetaData = results.getMetaData();
// System.out.println ("2");
        frs.setRSMD(resultsMetaData);
        //resultsStringBuffer.append(getOutputBaseDIR() + " "+ getOutputAppendage());
        FITSWriter fw = new FITSWriter();

	    //resultsStringBuffer.append("heretada");
        //addToResultsHTML(resultsStringBuffer.toString());
        ParseVOT pVOT=new ParseVOT();
        resultsNumColumns = resultsMetaData.getColumnCount();



        rowsWrittenUpperLimit=((int)Math.round(ROWSWRITTEN_UPPERLIMIT/(resultsNumColumns*1000.))*1000)-1;
        if (rowsWrittenUpperLimit < 1 || rowsWrittenUpperLimit > ROWSWRITTEN_UPPERLIMIT)
        {
			rowsWrittenUpperLimit=2;
		}

        rowsTotalUpperLimit=FACTOR*(rowsWrittenUpperLimit+1);
       // System.out.println(rowsWrittenUpperLimit);
        columnNames = new String[resultsNumColumns];

        switch (OutputFormatCache)
        {
        case OUTPUT_NONE:
          break; // case OUTPUT_NONE does nothing
        case OUTPUT_FITS:
          FITSName = "results" + uniqID ; //getNextCSVNumber()
          FITSFileName=FITSName+".fits";
          File FITSFile=new File(getOutputBaseDIR() + getOutputAppendage()+FITSFileName);
		  		if (FITSFile.exists()){
		  			FITSFile.delete();
		  		}


          fw.setFileName(getOutputBaseDIR() + getOutputAppendage()+FITSFileName);
          fw.setRSMD(resultsMetaData);

          break;
        case OUTPUT_CSV:
          CSVName = "results" + uniqID ; //getNextCSVNumber()

          switch (getOutputCompression())
          {
          case COMPRESS_NONE:
            CSVFileName = CSVName + ".csv";
            try
            {

              CSVOutputStream =
                new BufferedOutputStream(
                  new FileOutputStream(
                    getOutputBaseDIR() + getOutputAppendage() + CSVFileName
                  ),
                  OUTPUT_BUFFERSIZE
                );
            }
            catch (Exception e)
            {
              CSVOutputStream = null;
            }
            break; // case COMPRESS_NONE
          case COMPRESS_ZIP:
            CSVFileName = CSVName + ".zip";
            try
            {
              CSVOutputStream =
                new ZipOutputStream (
                  new BufferedOutputStream (
                    new FileOutputStream (
                      getOutputBaseDIR() + getOutputAppendage() + CSVFileName
                    ),
                    OUTPUT_BUFFERSIZE
                  )
                );

              // Prepare the zip
              ((ZipOutputStream) CSVOutputStream).setLevel(9);
              ((ZipOutputStream) CSVOutputStream).putNextEntry(
                                   new ZipEntry(CSVName + ".csv")
                                 );
            }
            catch (Exception e)
            {
              CSVOutputStream = null;
            }
            break; // case COMPRESS_ZIP
          case COMPRESS_GZIP:
            CSVFileName = CSVName + ".csv.gz";
            try
            {
              CSVOutputStream =
                new GZIPOutputStream(
                  new BufferedOutputStream(
                    new FileOutputStream(
                      getOutputBaseDIR() + getOutputAppendage() + CSVFileName
                    ),
                    OUTPUT_BUFFERSIZE
                  )
                );
            }
            catch (Exception e)
            {
              CSVOutputStream = null;
            }
            break; // case COMPRESS_GZIP
          } // switch

          if (CSVOutputStream != null)
          {
            try
            {
              CSVWriter = new PrintWriter(CSVOutputStream, true);
            }
            catch (Exception e)
            {
              CSVWriter = null;
            };
          };

          break; // case OUTPUT_CSV




        case OUTPUT_VOT:
        //pVOT=new ParseVOT();
        pVOT.setRSMD(resultsMetaData);
          CSVName = "results" + uniqID ; //getNextCSVNumber()

          switch (getOutputCompression())
          {
          case COMPRESS_NONE:
            CSVFileName = CSVName + ".xml";
            try
            {

              CSVOutputStream =
                new BufferedOutputStream(
                  new FileOutputStream(
                    getOutputBaseDIR() + getOutputAppendage() + CSVFileName
                  ),
                  OUTPUT_BUFFERSIZE
                );
            }
            catch (Exception e)
            {
              CSVOutputStream = null;
            }
            break; // case COMPRESS_NONE
          case COMPRESS_ZIP:
            CSVFileName = CSVName + ".zip";
            try
            {
              CSVOutputStream =
                new ZipOutputStream (
                  new BufferedOutputStream (
                    new FileOutputStream (
                      getOutputBaseDIR() + getOutputAppendage() + CSVFileName
                    ),
                    OUTPUT_BUFFERSIZE
                  )
                );

              // Prepare the zip
              ((ZipOutputStream) CSVOutputStream).setLevel(9);
              ((ZipOutputStream) CSVOutputStream).putNextEntry(
                                   new ZipEntry(CSVName + ".xml")
                                 );
            }
            catch (Exception e)
            {
              CSVOutputStream = null;
            }
            break; // case COMPRESS_ZIP
          case COMPRESS_GZIP:
            CSVFileName = CSVName + ".xml.gz";
            try
            {
              CSVOutputStream =
                new GZIPOutputStream(
                  new BufferedOutputStream(
                    new FileOutputStream(
                      getOutputBaseDIR() + getOutputAppendage() + CSVFileName
                    ),
                    OUTPUT_BUFFERSIZE
                  )
                );
            }
            catch (Exception e)
            {
              CSVOutputStream = null;
            }
            break; // case COMPRESS_GZIP
          } // switch

          if (CSVOutputStream != null)
          {
            try
            {
              CSVWriter = new PrintWriter(CSVOutputStream, true);
            }
            catch (Exception e)
            {
              CSVWriter = null;
            };
          };
          CSVWriter.println(pVOT.getVOTHeader());
		  CSVWriter.println(pVOT.setRSMD(resultsMetaData));
          break; // case OUTPUT_VOT



        } // switch (OutputFormatCache)

// System.out.println ("3");


        resultsStringBuffer.append(
          getPreTableHTML()
        );

        resultsStringBuffer.append(
	   "<form action=\""+SIXDFSchema.BASECGIURL+"pdf.cgi\" method=post> <input type=\"hidden\" name=\"release\" value=\"dr3\"><input type=\"hidden\" name=\"uniqID\" value=\""+uniqID+"\" >"+
         "    <table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" " +
                     "bgcolor=\"" + getHTMLTableColorRowOdd() + "\"> \n"
        );

        boolean GenerateHTML = true; // Set properly elsewhere...
        boolean doForm = false;
        boolean RAPresent= false;
        boolean DecPresent= false;
        int RACol =0;
        int DecCol=0;
        if (resultsNumColumns > 0) // We have at least one column returned!!!
        {
          if (GenerateHTML == true)
          {
            resultsStringBuffer.append(
              "      <tr bgcolor=\"" + getHTMLTableColorRowHeader() + "\"> \n"
            );

            // Insert (if any) extra cells before real data, can be overridden
            // in subclasses - defaults to printing the row number...
            resultsStringBuffer.append(
              getInsertedTableCellsHeaderHTML()
            );
          };

/*         for (int column = 1; column <= resultsNumColumns; column++)
          {
            columnNames[column - 1] = resultsMetaData.getColumnName(column);

            if (columnNames[column - 1].equalsIgnoreCase("RA"))
            {
				RACol=column-1;
				RAPresent= true;
			}

            if (columnNames[column - 1].equalsIgnoreCase("DEC"))
            {
				DecCol=column-1;
				DecPresent= true;
			}
		}



            if (GenerateHTML == true && RAPresent && DecPresent)
            {

                resultsStringBuffer.append(
                  "        <th nowrap=\"nowrap\">" +
                  "&nbsp;" +
                  "</th> \n"
                );
            };

*/

          for (int column = 1; column <= resultsNumColumns; column++)
          {
            columnNames[column - 1] = resultsMetaData.getColumnName(column);


            if (GenerateHTML == true)
            {
              if (columnNames[column - 1].length() < 1)
              {
                resultsStringBuffer.append(
                  "        <th nowrap=\"nowrap\">" +
                  "&nbsp;" +
                  "</th> \n"
                );
              }
              else
              {
                resultsStringBuffer.append(
                  "        <th nowrap=\"nowrap\">" +
                  columnNames[column-1] +
                  "</th> \n"
                );
              };
            };

            // Also, if needed, generate the header row for the CSV file...
            if ((OutputFormatCache == OUTPUT_CSV) && (CSVWriter != null))
            {
              if (column == resultsNumColumns)
              {
                CSVWriter.println(
                  columnNames[column-1]
                );
              }
              else
              {
                CSVWriter.print(
                  columnNames[column-1] + ","
                );
              };
            }; // if (GenerateCSV == true)

          };

          if (GenerateHTML == true)
          {
            resultsStringBuffer.append("      </tr> \n");
          }
        };

        columnFormats = getSQLDataFormatterArray();

        // Verify if the query is OK before continuing...
        verifyQueryAndOptions();

        // Now process the results (if any)...
        row = 0;
        String [] strArray;
        strArray=new String[resultsNumColumns];

        while (
                (!isQueryTerminated()) &&
                ( (OutputFormatCache != OUTPUT_NONE) && row <= rowsWrittenUpperLimit ||
                (GenerateHTML == true) ) &&
                (results.next())
              )
        {
          row++;
          GenerateHTML = ((row <= rowsHTML) && (row <= SQLROWS_UPPERLIMIT));

          // Put in the start of the HTML data row, before doing the FOR...
          // and also the first data element (the generated row number)...
          if (GenerateHTML == true)
          {
            if ((row % 2) == 0)
            {
              resultsStringBuffer.append(
                "      <tr bgcolor=\"" + getHTMLTableColorRowEven() +
                "\" class=\"RowEven\"> \n"
              );
            }
            else
            {
              resultsStringBuffer.append(
                "      <tr bgcolor=\"" + getHTMLTableColorRowOdd() +
                "\" class=\"RowOdd\"> \n"
              );
            };

            resultsStringBuffer.append(
              getInsertedTableCellsDataHTML()
            );
          };

          // Now that the headers are out of the way, we can do what we like
          // with the data for each of the columns (of this row) in turn...

          if ( (GenerateHTML == true) || (OutputFormatCache == OUTPUT_CSV) || (OutputFormatCache == OUTPUT_VOT))
          {
          strArray=frs.setRS(results);
	  		}

		  if ((OutputFormatCache == OUTPUT_FITS) )
		     {
		  		fw.setRS(results);
			}

          if (RAPresent && DecPresent && GenerateHTML)
          {
              resultsStringBuffer.append(
                 "        <td align=\"right\" nowrap=\"nowrap\">" +
                 "<input type=checkbox name=ch value=\""+strArray[RACol].trim()+":"+strArray[DecCol].trim()+"\">" +
                 "</td> \n"
              );
		  }



 			for (int column = 1; column <= resultsNumColumns; column++)
          {
            // Extract the raw data for this column as a string (safest as string)




  		/*	if (results.getString(column) == null)
            {
              columnData = null;
            }
            else
            {
              columnData = results.getString(column).trim();
            };*/




            // Firstly, if we are still within our requested display limits
            // generate a header row for the HTML table for this database row...
            if (GenerateHTML == true)
            {
			  columnData = strArray[column-1];
              resultsStringBuffer.append(
                "        <td align=\"right\" nowrap=\"nowrap\">" +
                columnFormats[column - 1].formatDataForHTML(columnData) +
                "</td> \n"
              );
            }; // if (GenerateHTML == true)



            // Then, with the same returned data, we now write it out to the CSV...
            if ((OutputFormatCache == OUTPUT_CSV) && (CSVWriter != null))
            {
			 //columnData = strArray[column-1];
              if (column == resultsNumColumns)
              {
                CSVWriter.println(
                  //columnFormats[column - 1].formatDataForCSV(columnData)
                  //strArray[column-1]
                  columnFormats[column - 1].formatDataForCSV(strArray[column-1])
                );
              }
              else
              {
                CSVWriter.print(
                  //columnFormats[column - 1].formatDataForCSV(columnData) + ","
                  //strArray[column-1]+","
                  columnFormats[column - 1].formatDataForCSV(strArray[column-1]) + ","
                );
              };
            }; // if (GenerateCSV == true)




            if ((OutputFormatCache == OUTPUT_VOT) && (CSVWriter != null))
            {
			 //columnData = strArray[column-1];
			 if (column == 1)
			 {
				 CSVWriter.print(
					 "<TR><TD>"+strArray[column-1] +"</TD>"
					 );
				 }
              else if (column == resultsNumColumns)
              {
                CSVWriter.println(
                  "<TD>" + strArray[column-1] +"</TD></TR>"
                );
              }
              else
              {
                CSVWriter.print(
                  //columnFormats[column - 1].formatDataForCSV(columnData) + ","
                  "<TD>"+strArray[column-1]+"</TD>"
                );
              };
            }; // if (GenerateCSV == true)




          }; // for

          // Put in the start of the HTML data row, before doing the FOR...
          if (GenerateHTML == true)
          {
            resultsStringBuffer.append("      </tr> \n");
          };
        }; // END of while results.next()...

        // Now do an extra count of the total number of rows that would have
        // been returned if the above while loop was stopped prematurely. If
        // the above while cycled through all the rows, this will do nothing.
        rowsWrittenToFile=row;
        while (
                (!isQueryTerminated()) &&
                (getDoRowTotal()) &&
                (results.next() && row < rowsTotalUpperLimit)
              )
        {
          row++;
        };

        resultsStringBuffer.append(
          "    </table>\n\n"
        );





        resultsStringBuffer.append(
          getPostTableHTML()
        );

        if ((OutputFormatCache == OUTPUT_FITS))
        {
			fw.close();
			if (dataOutputCompression==2)
			{

				try {
				        // Create the GZIP output stream
				        String outFilename = getOutputBaseDIR() + getOutputAppendage()+FITSFileName+".gz";
				        GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(outFilename));

				        // Open the input file
				        String inFilename = getOutputBaseDIR() + getOutputAppendage()+FITSFileName;
				        FileInputStream in = new FileInputStream(inFilename);

				        // Transfer bytes from the input file to the GZIP output stream
				        byte[] buf = new byte[1024];
				        int len;
				        while ((len = in.read(buf)) > 0) {
				            out.write(buf, 0, len);
				        }
				        in.close();

				        // Complete the GZIP file
				        out.finish();
				        out.close();
				        FITSFileName=FITSName+".fits.gz";
				    }
				    catch (IOException e) {
    			}
			}
		}

        if ((OutputFormatCache == OUTPUT_CSV) && (CSVWriter != null))
        {
          CSVWriter.flush(); // Ensure all data is finished

          if (getOutputCompression() == COMPRESS_ZIP)
          {
            ((ZipOutputStream) CSVOutputStream).closeEntry();
          };

          CSVWriter.close();
        };



        if ((OutputFormatCache == OUTPUT_VOT) && (CSVWriter != null))
        {
			CSVWriter.print(pVOT.getFooter());

          CSVWriter.flush(); // Ensure all data is finished

          if (getOutputCompression() == COMPRESS_ZIP)
          {
            ((ZipOutputStream) CSVOutputStream).closeEntry();
          };

          CSVWriter.close();
        };




       	if (OutputFormatCache != OUTPUT_NONE)
       	{
			int rowtemp=rowsWrittenUpperLimit+1;
			if (row > rowsWrittenToFile){
				resultsStringBuffer.append("<p><b> NB The number of rows written to the output file "+
				"reached the maximum allowed "+ rowtemp + "</b>"
				);
			}
		}





        if (OutputFormatCache == OUTPUT_FITS)
        {
			resultsStringBuffer.append(
              "    <p> \n" +
              "      <img src=\"" + getOutputBaseURL() + "images/icons/save.gif\" \n" +
                         "alt=\"Save icon\" /> \n" +
              "      <a href=\"" + getOutputBaseURL() + getOutputAppendage() + FITSFileName + "\">" +
                       "Download Results File </a>, your results in a FITS file \n" +
              "      (Contains <b>"
            );

            if (rowsWrittenToFile == 1)
            {
              resultsStringBuffer.append("1 row</b>, ");
            }
            else
            {
              resultsStringBuffer.append(rowsWrittenToFile + " rows</b>, ");
            };
            File FITSFile =
			              new File(getOutputBaseDIR() + getOutputAppendage() + FITSFileName);

			            long FITSFileSize = FITSFile.length();

			            if (FITSFileSize > 1000000)
			            {
			              resultsStringBuffer.append(
			                HTMLStrings.numbersTo1DP.format(FITSFileSize/1024.0/1024.0) +
			                " MB) \n"
			              );
			            }
			            else if (FITSFileSize > 1000)
			            {
			              resultsStringBuffer.append(
			                HTMLStrings.numbersTo1DP.format(FITSFileSize/1024.0) +
			                " KB) \n"
			              );
			            }
			            else
			            {
			              resultsStringBuffer.append(FITSFileSize + " bytes) \n");
			            };

            resultsStringBuffer.append("</p> \n\n");

            resultsStringBuffer.append(
              "    <p> \n" +
              "<img src=\"http://thoth.roe.ac.uk/ssa/topcat.gif\">"+
              "<a href=\"http://grendel12.roe.ac.uk:8080/ssa/launch.jsp?file=" +
              getOutputBaseURL() + getOutputAppendage() + FITSFileName + "\"" +
              ">Launch file in Topcat</a> (requires Java 1.5 and Java Web Start, approx 12Mb download for Topcat application)<p>"
              );
		}

        if (OutputFormatCache == OUTPUT_CSV || OutputFormatCache == OUTPUT_VOT )
        {
          if (CSVWriter != null)
          {
            resultsStringBuffer.append(
              "    <p> \n" +
              "      <img src=\"" + getOutputBaseURL() + "images/icons/save.gif\" \n" +
                         "alt=\"Save icon\" /> \n" +
              "      <a href=\"" + getOutputBaseURL() + getOutputAppendage() + CSVFileName + "\">" +
                       "Download Results File </a>,  \n" +
              "      (Contains <b>"
            );



            if (rowsWrittenToFile == 1)
            {
              resultsStringBuffer.append("1 row</b>, ");
            }
            else
            {
              resultsStringBuffer.append(rowsWrittenToFile + " rows</b>, ");
            };

            File CSVFile =
              new File(getOutputBaseDIR() + getOutputAppendage() + CSVFileName);

            long CSVFileSize = CSVFile.length();

            if (CSVFileSize > 1000000)
            {
              resultsStringBuffer.append(
                HTMLStrings.numbersTo1DP.format(CSVFileSize/1024.0/1024.0) +
                " MB) \n"
              );
            }
            else if (CSVFileSize > 1000)
            {
              resultsStringBuffer.append(
                HTMLStrings.numbersTo1DP.format(CSVFileSize/1024.0) +
                " KB) \n"
              );
            }
            else
            {
              resultsStringBuffer.append(CSVFileSize + " bytes) \n");
            };

            resultsStringBuffer.append("</p> \n\n");

            if (OutputFormatCache == OUTPUT_VOT )
            {
            resultsStringBuffer.append(
              "    <p> \n" +
              "<img src=\"http://thoth.roe.ac.uk/ssa/topcat.gif\">"+
              "<a href=\"http://grendel12.roe.ac.uk:8080/ssa/launch.jsp?file=" +
              getOutputBaseURL() + getOutputAppendage() + CSVFileName + "\"" +
              ">Launch file in Topcat</a> (requires Java 1.5 and Java Web Start, approx 12Mb download for Topcat application)<p>"
              );
		  }


          }
          else
          {
            resultsStringBuffer.append(
              "    <p> \n" +
              "      Sorry - the server experienced an unknown problem while creating " +
              "your file, please try again later.\n" +
              "    </p> \n\n"
            );
          };
        };


        if (RAPresent && DecPresent && row > 0)
        {
			
 //
        }
	

        resultsStringBuffer.append(
                "<SCRIPT LANGUAGE=\"javascript\">"+
        		"function checkAll(){"+
        		"for (var i=0;i<document.forms[0].show.length;i++)"+
        		"document.forms[0].show[i].checked = true;}"+
        		"function uncheckAll(){"+
        		"for (var i=0;i<document.forms[0].show.length;i++)"+
        		"document.forms[0].show[i].checked = false;}"+
        		"</script>"+
        		"If your query returned the specID or targetname attributes then to generate a PDF file of the spectra " +
        		"and thumbnail images of objects listed above select the ones required (max 300) and click on getPDF. "+
        		"Use sparingly as generating the PDF file for a few hundred objects can take several minutes"+
        		"<p><input class=\"FontSans\" type=\"button\" name=\"selectall\" value=\"&nbsp;check all&nbsp;\" onclick=\"checkAll();\">"+
        		"&nbsp&nbsp;<input class=\"FontSans\" type=\"button\" name=\"selectall\" value=\"uncheck all\" onclick=\"uncheckAll();\"> "+
                "<input class=\"FontSans\" type=submit value=\"getPDF\">"+
          "</form>\n"
        );


        // Finally, copy the results into the string...
        clearResultsHTML();
        addToResultsHTML(resultsStringBuffer.toString());
      }
      catch (SQLException se)
      {
        System.err.println(
          "SQLRetrieverThread::run(): an SQLException -> " + se.getMessage() + se.getErrorCode()
        );

        generateErrorHTML(
          "<b>SQL Error:</b> " + se.getMessage()
        );
      }
      catch (Exception e)
      {
        // General exception catch-all. Any exceptions in run() that fall
        // through are trapped and neatly formatted here...

        System.err.println(
          "SQLRetrieverThread::run(): Exception -> " + e.toString()
        );

        generateErrorHTML(
            "<b>Error in run():</b> " + e.toString()
        );
      };
    }
    finally
    {


      setQueryProcessing(false);
      try {
		  databaseConnection.close();
		 // System.out.println("here1"+Calendar.getInstance().getTime());
	  }
	  catch (SQLException se)
        {
        };

      // Free up used resources...
      CSVWriter = null;
      CSVOutputStream = null;
      resultsStringBuffer = null;
      resultsMetaData = null;
      if (results != null)
      {
        try
        {
			//databaseQuery.cancel(); // was commented out
        	results.close();
        	//System.out.println("here2"+Calendar.getInstance().getTime());

        }
        catch (SQLException se)
        {
			//System.out.println("here3"+Calendar.getInstance().getTime() + se.getMessage());
        };
      };

      results = null;

      if (databaseQuery != null)
      {
        try
        {
			//databaseQuery.cancel();
        	databaseQuery.close();
//        	System.out.println("here3"+Calendar.getInstance().getTime() );
        }
        catch (SQLException se)
        {
        };
      };

      databaseQuery = null;


      databaseConnection = null;


	  if (isMightBeBackground())
	  {
	  int waitCount=0;
      while (getEmailAddress()==null && waitCount < 15)
      {
      		waitCount=waitCount+1;
	  			try
	                {
	                  Thread.sleep(1000);
	                }
	                catch (InterruptedException ie)
	                {
	                  // Do nothing when interrupted...
	                }

		} //end of while

		//  System.out.println("email "+getEmailAddress() +" " + Calendar.getInstance().getTime() );
		 if (getEmailAddress()!=null){
     	 if (getEmailAddress().length() > 3){
	  		 	String HTMLFileName=getOutputBaseDIR() + getOutputAppendage()+ "results" + uniqID + ".html";
	  			File HTMLFile=new File(HTMLFileName);
	  				try{
	  				HTMLFile.createNewFile();
	  				FileWriter HTMLfw=new FileWriter(HTMLFile);
	  				PrintWriter HTMLpw = new PrintWriter(HTMLfw);
	  				HTMLpw.println(HTMLStrings.getHTMLHeadWithScript(
                "6dFGS SQL Query Results",
  //              "http://grendel12.roe.ac.uk/~avo/ssa/" + "configurestyles.js"));
  				SIXDFSchema.BASEURL + "configurestyles.js"));


					HTMLpw.println("<h3>6dFGS Database - SQL Query Results</h3>");
	  				HTMLpw.println(htmlResults.toString());
	  				HTMLpw.println("</body> </html>");
	  				HTMLfw.close();

	  				}
	  				catch(IOException e){
	  				}
	  				try
	  				{
	  				java.util.Properties properties = System.getProperties();
      				Session session = Session.getInstance(properties, null);
      		      // Construct a message
				    MimeMessage message = new MimeMessage(session);
      				message.setFrom(new InternetAddress("6df@roe.ac.uk"));
      				message.addRecipient(Message.RecipientType.TO,
      				new InternetAddress(getEmailAddress()));
      				message.addRecipient(Message.RecipientType.BCC,
      				new InternetAddress("mar@roe.ac.uk"));
     			 	message.setSubject("6dFGS query results");
     			 	message.setText("The results of your query can be retrieved from \n" +
     			 	getOutputBaseURL() + getOutputAppendage() +"results"+ uniqID + ".html");

     			 	//message.setText(getOutputBaseURL() + getOutputAppendage() +"results"+ uniqID + ".html");

      				// Connect to the transport
      				Transport transport = session.getTransport("smtp");
      				transport.connect("thoth-p", "", "");

      				// Send the message and close the connection
      				transport.sendMessage(message, message.getAllRecipients());
      				transport.close();
					}
					catch (MessagingException me)
					{
						//System.out.println(me.getMessage());
					}



			}
		}



	} // end of ismightbebackground

System.gc();

    };
  };
}; // END of class SQLRetrieverThread