Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that searches a relational DB for elements and
4
 *             attributes that have free text matches a query string,
5
 *             or structured query matches to a path specified node in the
6
 *             XML hierarchy.  It returns a result set consisting of the
7
 *             document ID for each document that satisfies the query
8
 *  Copyright: 2000 Regents of the University of California and the
9
 *             National Center for Ecological Analysis and Synthesis
10
 *    Authors: Matt Jones
11
 *
12
 *   '$Author: daigle $'
13
 *     '$Date: 2009-12-18 13:26:26 -0800 (Fri, 18 Dec 2009) $'
14
 * '$Revision: 5165 $'
15
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 2 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
 */
30

    
31
package edu.ucsb.nceas.metacat;
32

    
33
import java.io.*;
34
import java.util.zip.*;
35
import java.sql.PreparedStatement;
36
import java.sql.ResultSet;
37
import java.sql.SQLException;
38
import java.util.*;
39

    
40
import javax.servlet.ServletOutputStream;
41
import javax.servlet.http.HttpServletResponse;
42
import javax.servlet.http.HttpSession;
43

    
44
import org.apache.log4j.Logger;
45

    
46
import org.w3c.dom.*;
47
import javax.xml.parsers.DocumentBuilderFactory;
48
import org.xml.sax.InputSource;
49
import org.w3c.dom.ls.*;
50

    
51
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlInterface;
52
import edu.ucsb.nceas.metacat.database.DBConnection;
53
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
54
import edu.ucsb.nceas.metacat.properties.PropertyService;
55
import edu.ucsb.nceas.metacat.util.AuthUtil;
56
import edu.ucsb.nceas.metacat.util.DocumentUtil;
57
import edu.ucsb.nceas.metacat.util.MetacatUtil;
58
import edu.ucsb.nceas.morpho.datapackage.Triple;
59
import edu.ucsb.nceas.morpho.datapackage.TripleCollection;
60
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
61

    
62

    
63
/**
64
 * A Class that searches a relational DB for elements and attributes that have
65
 * free text matches a query string, or structured query matches to a path
66
 * specified node in the XML hierarchy. It returns a result set consisting of
67
 * the document ID for each document that satisfies the query
68
 */
69
public class DBQuery
70
{
71

    
72
    static final int ALL = 1;
73

    
74
    static final int WRITE = 2;
75

    
76
    static final int READ = 4;
77

    
78
    //private Connection conn = null;
79
    private String parserName = null;
80

    
81
    private Logger logMetacat = Logger.getLogger(DBQuery.class);
82

    
83
    /** true if the metacat spatial option is installed **/
84
    private final boolean METACAT_SPATIAL = true;
85

    
86
    /** useful if you just want to grab a list of docids. Since the docids can be very long,
87
         it is a vector of vector  **/
88
    Vector docidOverride = new Vector();
89
    
90
    // a hash table serves as query reuslt cache. Key of hashtable
91
    // is a query string and value is result xml string
92
    private static Hashtable queryResultCache = new Hashtable();
93
    
94
    // Capacity of the query result cache
95
    private static final int QUERYRESULTCACHESIZE;
96
    static {
97
    	int qryRsltCacheSize = 0;
98
    	try {
99
    		qryRsltCacheSize = Integer.parseInt(PropertyService.getProperty("database.queryresultCacheSize"));
100
    	} catch (PropertyNotFoundException pnfe) {
101
    		System.err.println("Could not get QUERYRESULTCACHESIZE property in static block: "
102
					+ pnfe.getMessage());
103
    	}
104
    	QUERYRESULTCACHESIZE = qryRsltCacheSize;
105
    }
106
    
107

    
108
    // Size of page for non paged query
109
    private static final int NONPAGESIZE = 99999999;
110
    /**
111
     * the main routine used to test the DBQuery utility.
112
     * <p>
113
     * Usage: java DBQuery <xmlfile>
114
     *
115
     * @param xmlfile the filename of the xml file containing the query
116
     */
117
    static public void main(String[] args)
118
    {
119

    
120
        if (args.length < 1) {
121
            System.err.println("Wrong number of arguments!!!");
122
            System.err.println("USAGE: java DBQuery [-t] [-index] <xmlfile>");
123
            return;
124
        } else {
125
            try {
126

    
127
                int i = 0;
128
                boolean showRuntime = false;
129
                boolean useXMLIndex = false;
130
                if (args[i].equals("-t")) {
131
                    showRuntime = true;
132
                    i++;
133
                }
134
                if (args[i].equals("-index")) {
135
                    useXMLIndex = true;
136
                    i++;
137
                }
138
                String xmlfile = args[i];
139

    
140
                // Time the request if asked for
141
                double startTime = System.currentTimeMillis();
142

    
143
                // Open a connection to the database
144
                //Connection dbconn = util.openDBConnection();
145

    
146
                double connTime = System.currentTimeMillis();
147

    
148
                // Execute the query
149
                DBQuery queryobj = new DBQuery();
150
                FileReader xml = new FileReader(new File(xmlfile));
151
                Hashtable nodelist = null;
152
                //nodelist = queryobj.findDocuments(xml, null, null, useXMLIndex);
153

    
154
                // Print the reulting document listing
155
                StringBuffer result = new StringBuffer();
156
                String document = null;
157
                String docid = null;
158
                result.append("<?xml version=\"1.0\"?>\n");
159
                result.append("<resultset>\n");
160

    
161
                if (!showRuntime) {
162
                    Enumeration doclist = nodelist.keys();
163
                    while (doclist.hasMoreElements()) {
164
                        docid = (String) doclist.nextElement();
165
                        document = (String) nodelist.get(docid);
166
                        result.append("  <document>\n    " + document
167
                                + "\n  </document>\n");
168
                    }
169

    
170
                    result.append("</resultset>\n");
171
                }
172
                // Time the request if asked for
173
                double stopTime = System.currentTimeMillis();
174
                double dbOpenTime = (connTime - startTime) / 1000;
175
                double readTime = (stopTime - connTime) / 1000;
176
                double executionTime = (stopTime - startTime) / 1000;
177
                if (showRuntime) {
178
                    System.out.print("  " + executionTime);
179
                    System.out.print("  " + dbOpenTime);
180
                    System.out.print("  " + readTime);
181
                    System.out.print("  " + nodelist.size());
182
                    System.out.println();
183
                }
184
                //System.out.println(result);
185
                //write into a file "result.txt"
186
                if (!showRuntime) {
187
                    File f = new File("./result.txt");
188
                    FileWriter fw = new FileWriter(f);
189
                    BufferedWriter out = new BufferedWriter(fw);
190
                    out.write(result.toString());
191
                    out.flush();
192
                    out.close();
193
                    fw.close();
194
                }
195

    
196
            } catch (Exception e) {
197
                System.err.println("Error in DBQuery.main");
198
                System.err.println(e.getMessage());
199
                e.printStackTrace(System.err);
200
            }
201
        }
202
    }
203

    
204
    /**
205
     * construct an instance of the DBQuery class
206
     *
207
     * <p>
208
     * Generally, one would call the findDocuments() routine after creating an
209
     * instance to specify the search query
210
     * </p>
211
     *
212

    
213
     * @param parserName the fully qualified name of a Java class implementing
214
     *            the org.xml.sax.XMLReader interface
215
     */
216
    public DBQuery() throws PropertyNotFoundException
217
    {
218
        String parserName = PropertyService.getProperty("xml.saxparser");
219
        this.parserName = parserName;
220
    }
221

    
222
    /**
223
     * 
224
     * Construct an instance of DBQuery Class
225
     * BUT accept a docid Vector that will supersede
226
     * the query.printSQL() method
227
     *
228
     * If a docid Vector is passed in,
229
     * the docids will be used to create a simple IN query 
230
     * without the multiple subselects of the printSQL() method
231
     *
232
     * Using this constructor, we just check for 
233
     * a docidOverride Vector in the findResultDoclist() method
234
     *
235
     * @param docids List of docids to display in the resultset
236
     */
237
    public DBQuery(Vector docids) throws PropertyNotFoundException
238
    {
239
    	// since the query will be too long to be handled, so we divided the 
240
    	// docids vector into couple vectors.
241
    	int size = (new Integer(PropertyService.getProperty("database.appResultsetSize"))).intValue();
242
    	logMetacat.info("DBQuery.DBQuery - The size of select doicds is "+docids.size());
243
    	logMetacat.info("DBQuery.DBQuery - The application result size in metacat.properties is "+size);
244
    	Vector subset = new Vector();
245
    	if (docids != null && docids.size() > size)
246
    	{
247
    		int index = 0;
248
    		for (int i=0; i< docids.size(); i++)
249
    		{
250
    			
251
    			if (index < size)
252
    			{  	
253
    				subset.add(docids.elementAt(i));
254
    				index ++;
255
    			}
256
    			else
257
    			{
258
    				docidOverride.add(subset);
259
    				subset = new Vector();
260
    				subset.add(docids.elementAt(i));
261
    			    index = 1;
262
    			}
263
    		}
264
    		if (!subset.isEmpty())
265
    		{
266
    			docidOverride.add(subset);
267
    		}
268
    		
269
    	}
270
    	else
271
    	{
272
    		this.docidOverride.add(docids);
273
    	}
274
        
275
        String parserName = PropertyService.getProperty("xml.saxparser");
276
        this.parserName = parserName;
277
    }
278

    
279
  /**
280
   * Method put the search result set into out printerwriter
281
   * @param resoponse the return response
282
   * @param out the output printer
283
   * @param params the paratermer hashtable
284
   * @param user the user name (it maybe different to the one in param)
285
   * @param groups the group array
286
   * @param sessionid  the sessionid
287
   */
288
  public void findDocuments(HttpServletResponse response,
289
                                       PrintWriter out, Hashtable params,
290
                                       String user, String[] groups,
291
                                       String sessionid) throws PropertyNotFoundException
292
  {
293
    boolean useXMLIndex = (new Boolean(PropertyService.getProperty("database.usexmlindex")))
294
               .booleanValue();
295
    findDocuments(response, out, params, user, groups, sessionid, useXMLIndex);
296

    
297
  }
298

    
299

    
300
    /**
301
     * Method put the search result set into out printerwriter
302
     * @param resoponse the return response
303
     * @param out the output printer
304
     * @param params the paratermer hashtable
305
     * @param user the user name (it maybe different to the one in param)
306
     * @param groups the group array
307
     * @param sessionid  the sessionid
308
     */
309
    public void findDocuments(HttpServletResponse response,
310
                                         PrintWriter out, Hashtable params,
311
                                         String user, String[] groups,
312
                                         String sessionid, boolean useXMLIndex)
313
    {
314
      int pagesize = 0;
315
      int pagestart = 0;
316
      long transferWarnLimit = 0; 
317
      
318
      if(params.containsKey("pagesize") && params.containsKey("pagestart"))
319
      {
320
        String pagesizeStr = ((String[])params.get("pagesize"))[0];
321
        String pagestartStr = ((String[])params.get("pagestart"))[0];
322
        if(pagesizeStr != null && pagestartStr != null)
323
        {
324
          pagesize = (new Integer(pagesizeStr)).intValue();
325
          pagestart = (new Integer(pagestartStr)).intValue();
326
        }
327
      }
328
      
329
      String xmlquery = null;
330
      String qformat = null;
331
      // get query and qformat
332
      try {
333
    	xmlquery = ((String[])params.get("query"))[0];
334

    
335
        logMetacat.info("DBQuery.findDocuments - SESSIONID: " + sessionid);
336
        logMetacat.info("DBQuery.findDocuments - xmlquery: " + xmlquery);
337
        qformat = ((String[])params.get("qformat"))[0];
338
        logMetacat.info("DBQuery.findDocuments - qformat: " + qformat);
339
      }
340
      catch (Exception ee)
341
      {
342
        logMetacat.error("DBQuery.findDocuments - Couldn't retrieve xmlquery or qformat value from "
343
                  +"params hashtable in DBQuery.findDocuments: "
344
                  + ee.getMessage()); 
345
      }
346
      // Get the XML query and covert it into a SQL statment
347
      QuerySpecification qspec = null;
348
      if ( xmlquery != null)
349
      {
350
         xmlquery = transformQuery(xmlquery);
351
         try
352
         {
353
           qspec = new QuerySpecification(xmlquery,
354
                                          parserName,
355
                                          PropertyService.getProperty("document.accNumSeparator"));
356
         }
357
         catch (Exception ee)
358
         {
359
           logMetacat.error("DBQuery.findDocuments - error generating QuerySpecification object: "
360
                                    + ee.getMessage());
361
         }
362
      }
363

    
364

    
365

    
366
      if (qformat != null && qformat.equals(MetacatUtil.XMLFORMAT))
367
      {
368
        //xml format
369
        response.setContentType("text/xml");
370
        createResultDocument(xmlquery, qspec, out, user, groups, useXMLIndex, 
371
          pagesize, pagestart, sessionid);
372
      }//if
373
      else
374
      {
375
        //knb format, in this case we will get whole result and sent it out
376
        response.setContentType("text/html");
377
        PrintWriter nonout = null;
378
        StringBuffer xml = createResultDocument(xmlquery, qspec, nonout, user,
379
                                                groups, useXMLIndex, pagesize, 
380
                                                pagestart, sessionid);
381
        
382
        //transfer the xml to html
383
        try
384
        {
385
         long startHTMLTransform = System.currentTimeMillis();
386
         DBTransform trans = new DBTransform();
387
         response.setContentType("text/html");
388

    
389
         // if the user is a moderator, then pass a param to the 
390
         // xsl specifying the fact
391
         if(AuthUtil.isModerator(user, groups)){
392
        	 params.put("isModerator", new String[] {"true"});
393
         }
394

    
395
         trans.transformXMLDocument(xml.toString(), "-//NCEAS//resultset//EN",
396
                                 "-//W3C//HTML//EN", qformat, out, params,
397
                                 sessionid);
398
         long transformRunTime = System.currentTimeMillis() - startHTMLTransform;
399
         
400
         transferWarnLimit = Long.parseLong(PropertyService.getProperty("dbquery.transformTimeWarnLimit"));
401
         
402
         if (transformRunTime > transferWarnLimit) {
403
         	logMetacat.warn("DBQuery.findDocuments - The time to transfrom resultset from xml to html format is "
404
                  		                             + transformRunTime);
405
         }
406
          MetacatUtil.writeDebugToFile("---------------------------------------------------------------------------------------------------------------Transfrom xml to html  "
407
                             + transformRunTime);
408
          MetacatUtil.writeDebugToDelimiteredFile(" " + transformRunTime, false);
409
        }
410
        catch(Exception e)
411
        {
412
         logMetacat.error("DBQuery.findDocuments - Error in MetaCatServlet.transformResultset:"
413
                                +e.getMessage());
414
         }
415

    
416
      }//else
417

    
418
  }
419
  
420
  /**
421
   * Transforms a hashtable of documents to an xml or html result and sent
422
   * the content to outputstream. Keep going untill hastable is empty. stop it.
423
   * add the QuerySpecification as parameter is for ecogrid. But it is duplicate
424
   * to xmlquery String
425
   * @param xmlquery
426
   * @param qspec
427
   * @param out
428
   * @param user
429
   * @param groups
430
   * @param useXMLIndex
431
   * @param sessionid
432
   * @return
433
   */
434
    public StringBuffer createResultDocument(String xmlquery,
435
                                              QuerySpecification qspec,
436
                                              PrintWriter out,
437
                                              String user, String[] groups,
438
                                              boolean useXMLIndex)
439
    {
440
    	return createResultDocument(xmlquery,qspec,out, user,groups, useXMLIndex, 0, 0,"");
441
    }
442

    
443
  /*
444
   * Transforms a hashtable of documents to an xml or html result and sent
445
   * the content to outputstream. Keep going untill hastable is empty. stop it.
446
   * add the QuerySpecification as parameter is for ecogrid. But it is duplicate
447
   * to xmlquery String
448
   */
449
  public StringBuffer createResultDocument(String xmlquery,
450
                                            QuerySpecification qspec,
451
                                            PrintWriter out,
452
                                            String user, String[] groups,
453
                                            boolean useXMLIndex, int pagesize,
454
                                            int pagestart, String sessionid)
455
  {
456
    DBConnection dbconn = null;
457
    int serialNumber = -1;
458
    StringBuffer resultset = new StringBuffer();
459

    
460
    //try to get the cached version first    
461
    // Hashtable sessionHash = MetaCatServlet.getSessionHash();
462
    // HttpSession sess = (HttpSession)sessionHash.get(sessionid);
463

    
464
    
465
    resultset.append("<?xml version=\"1.0\"?>\n");
466
    resultset.append("<resultset>\n");
467
    resultset.append("  <pagestart>" + pagestart + "</pagestart>\n");
468
    resultset.append("  <pagesize>" + pagesize + "</pagesize>\n");
469
    resultset.append("  <nextpage>" + (pagestart + 1) + "</nextpage>\n");
470
    resultset.append("  <previouspage>" + (pagestart - 1) + "</previouspage>\n");
471

    
472
    resultset.append("  <query>" + xmlquery + "</query>");
473
    //send out a new query
474
    if (out != null)
475
    {
476
      out.println(resultset.toString());
477
    }
478
    if (qspec != null)
479
    {
480
      try
481
      {
482

    
483
        //checkout the dbconnection
484
        dbconn = DBConnectionPool.getDBConnection("DBQuery.findDocuments");
485
        serialNumber = dbconn.getCheckOutSerialNumber();
486

    
487
        //print out the search result
488
        // search the doc list
489
        Vector givenDocids = new Vector();
490
        StringBuffer resultContent = new StringBuffer();
491
        if (docidOverride == null || docidOverride.size() == 0)
492
        {
493
        	logMetacat.debug("DBQuery.createResultDocument - Not in map query");
494
        	resultContent = findResultDoclist(qspec, out, user, groups,
495
                    dbconn, useXMLIndex, pagesize, pagestart, 
496
                    sessionid, givenDocids);
497
        }
498
        else
499
        {
500
        	logMetacat.debug("DBQuery.createResultDocument - In map query");
501
        	// since docid can be too long to be handled. We divide it into several parts
502
        	for (int i= 0; i<docidOverride.size(); i++)
503
        	{
504
        	   logMetacat.debug("DBQuery.createResultDocument - in loop===== "+i);
505
        		givenDocids = (Vector)docidOverride.elementAt(i);
506
        		StringBuffer subset = findResultDoclist(qspec, out, user, groups,
507
                        dbconn, useXMLIndex, pagesize, pagestart, 
508
                        sessionid, givenDocids);
509
        		resultContent.append(subset);
510
        	}
511
        }
512
           
513
        resultset.append(resultContent);
514
      } //try
515
      catch (IOException ioe)
516
      {
517
        logMetacat.error("DBQuery.createResultDocument - IO error: " + ioe.getMessage());
518
      }
519
      catch (SQLException e)
520
      {
521
        logMetacat.error("DBQuery.createResultDocument - SQL Error: " + e.getMessage());
522
      }
523
      catch (Exception ee)
524
      {
525
        logMetacat.error("DBQuery.createResultDocument - General exception: "
526
                                 + ee.getMessage());
527
        ee.printStackTrace();
528
      }
529
      finally
530
      {
531
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
532
      } //finally
533
    }//if
534
    String closeRestultset = "</resultset>";
535
    resultset.append(closeRestultset);
536
    if (out != null)
537
    {
538
      out.println(closeRestultset);
539
    }
540

    
541
    //default to returning the whole resultset
542
    return resultset;
543
  }//createResultDocuments
544

    
545
    /*
546
     * Find the doc list which match the query
547
     */
548
    private StringBuffer findResultDoclist(QuerySpecification qspec,
549
                                      PrintWriter out,
550
                                      String user, String[]groups,
551
                                      DBConnection dbconn, boolean useXMLIndex,
552
                                      int pagesize, int pagestart, String sessionid, Vector givenDocids)
553
                                      throws Exception
554
    {
555
      StringBuffer resultsetBuffer = new StringBuffer();
556
      String query = null;
557
      int count = 0;
558
      int index = 0;
559
      ResultDocumentSet docListResult = new ResultDocumentSet();
560
      PreparedStatement pstmt = null;
561
      String docid = null;
562
      String docname = null;
563
      String doctype = null;
564
      String createDate = null;
565
      String updateDate = null;
566
      StringBuffer document = null;
567
      boolean lastpage = false;
568
      int rev = 0;
569
      double startTime = 0;
570
      int offset = 1;
571
      long startSelectionTime = System.currentTimeMillis();
572
      ResultSet rs = null;
573
           
574
   
575
      // this is a hack for offset. in postgresql 7, if the returned docid list is too long,
576
      //the extend query which base on the docid will be too long to be run. So we 
577
      // have to cut them into different parts. Page query don't need it somehow.
578
      if (out == null)
579
      {
580
        // for html page, we put everything into one page
581
        offset =
582
            (new Integer(PropertyService.getProperty("database.webResultsetSize"))).intValue();
583
      }
584
      else
585
      {
586
          offset =
587
              (new Integer(PropertyService.getProperty("database.appResultsetSize"))).intValue();
588
      }
589

    
590
      /*
591
       * Check the docidOverride Vector
592
       * if defined, we bypass the qspec.printSQL() method
593
       * and contruct a simpler query based on a 
594
       * list of docids rather than a bunch of subselects
595
       */
596
      if ( givenDocids == null || givenDocids.size() == 0 ) {
597
          query = qspec.printSQL(useXMLIndex);
598
      } else {
599
          logMetacat.info("DBQuery.findResultDoclist - docid override " + givenDocids.size());
600
          StringBuffer queryBuffer = new StringBuffer( "SELECT docid,docname,doctype,date_created, date_updated, rev " );
601
          queryBuffer.append( " FROM xml_documents WHERE docid IN (" );
602
          for (int i = 0; i < givenDocids.size(); i++) {  
603
              queryBuffer.append("'");
604
              queryBuffer.append( (String)givenDocids.elementAt(i) );
605
              queryBuffer.append("',");
606
          }
607
          // empty string hack 
608
          queryBuffer.append( "'') " );
609
          query = queryBuffer.toString();
610
      } 
611
      String ownerQuery = getOwnerQuery(user);
612
      //logMetacat.debug("query: " + query);
613
      logMetacat.debug("DBQuery.findResultDoclist - owner query: " + ownerQuery);
614
      // if query is not the owner query, we need to check the permission
615
      // otherwise we don't need (owner has all permission by default)
616
      if (!query.equals(ownerQuery))
617
      {
618
        // set user name and group
619
        qspec.setUserName(user);
620
        qspec.setGroup(groups);
621
        // Get access query
622
        String accessQuery = qspec.getAccessQuery();
623
        if(!query.endsWith("WHERE")){
624
            query = query + accessQuery;
625
        } else {
626
            query = query + accessQuery.substring(4, accessQuery.length());
627
        }
628
        
629
      }
630
      logMetacat.debug("DBQuery.findResultDoclist - final selection query: " + query);
631
      String selectionAndExtendedQuery = null;
632
      // we only get cache for public
633
      if (user != null && user.equalsIgnoreCase("public") 
634
     		 && pagesize == 0 && PropertyService.getProperty("database.queryCacheOn").equals("true"))
635
      {
636
    	  selectionAndExtendedQuery = query +qspec.getReturnDocList()+qspec.getReturnFieldList();
637
   	      String cachedResult = getResultXMLFromCache(selectionAndExtendedQuery);
638
   	      logMetacat.debug("DBQuery.findResultDoclist - The key of query cache is " + selectionAndExtendedQuery);
639
   	      //System.out.println("==========the string from cache is "+cachedResult);
640
   	      if (cachedResult != null)
641
   	      {
642
   	    	logMetacat.info("DBQuery.findResultDoclist - result from cache !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
643
   	    	 if (out != null)
644
   	         {
645
   	             out.println(cachedResult);
646
   	         }
647
   	    	 resultsetBuffer.append(cachedResult);
648
   	    	 return resultsetBuffer;
649
   	      }
650
      }
651
      
652
      startTime = System.currentTimeMillis() / 1000;
653
      pstmt = dbconn.prepareStatement(query);
654
      rs = pstmt.executeQuery();
655

    
656
      double queryExecuteTime = System.currentTimeMillis() / 1000;
657
      logMetacat.debug("DBQuery.findResultDoclist - Time to execute select docid query is "
658
                    + (queryExecuteTime - startTime));
659
      MetacatUtil.writeDebugToFile("\n\n\n\n\n\nExecute selection query  "
660
              + (queryExecuteTime - startTime));
661
      MetacatUtil.writeDebugToDelimiteredFile(""+(queryExecuteTime - startTime), false);
662

    
663
      boolean tableHasRows = rs.next();
664
      
665
      if(pagesize == 0)
666
      { //this makes sure we get all results if there is no paging
667
        pagesize = NONPAGESIZE;
668
        pagestart = NONPAGESIZE;
669
      } 
670
      
671
      int currentIndex = 0;
672
      while (tableHasRows)
673
      {
674
        logMetacat.debug("DBQuery.findResultDoclist - getting result: " + currentIndex);
675
        docid = rs.getString(1).trim();
676
        logMetacat.debug("DBQuery.findResultDoclist -  processing: " + docid);
677
        docname = rs.getString(2);
678
        doctype = rs.getString(3);
679
        logMetacat.debug("DBQuery.findResultDoclist - processing: " + doctype);
680
        createDate = rs.getString(4);
681
        updateDate = rs.getString(5);
682
        rev = rs.getInt(6);
683
        
684
         Vector returndocVec = qspec.getReturnDocList();
685
       if (returndocVec.size() == 0 || returndocVec.contains(doctype))
686
        {
687
          logMetacat.debug("DBQuery.findResultDoclist - NOT Back tracing now...");
688
           document = new StringBuffer();
689

    
690
           String completeDocid = docid
691
                            + PropertyService.getProperty("document.accNumSeparator");
692
           completeDocid += rev;
693
           document.append("<docid>").append(completeDocid).append("</docid>");
694
           if (docname != null)
695
           {
696
               document.append("<docname>" + docname + "</docname>");
697
           }
698
           if (doctype != null)
699
           {
700
              document.append("<doctype>" + doctype + "</doctype>");
701
           }
702
           if (createDate != null)
703
           {
704
               document.append("<createdate>" + createDate + "</createdate>");
705
           }
706
           if (updateDate != null)
707
           {
708
             document.append("<updatedate>" + updateDate + "</updatedate>");
709
           }
710
           // Store the document id and the root node id
711
           
712
           docListResult.addResultDocument(
713
             new ResultDocument(docid, (String) document.toString()));
714
           logMetacat.info("DBQuery.findResultDoclist - real result: " + docid);
715
           currentIndex++;
716
           count++;
717
        }//else
718
        
719
        // when doclist reached the offset number, send out doc list and empty
720
        // the hash table
721
        if (count == offset && pagesize == NONPAGESIZE)
722
        { //if pagesize is not 0, do this later.
723
          //reset count
724
          //logMetacat.warn("############doing subset cache");
725
          count = 0;
726
          handleSubsetResult(qspec, resultsetBuffer, out, docListResult,
727
                              user, groups,dbconn, useXMLIndex);
728
          //reset docListResult
729
          docListResult = new ResultDocumentSet();
730
        }
731
       
732
       logMetacat.debug("DBQuery.findResultDoclist - currentIndex: " + currentIndex);
733
       logMetacat.debug("DBQuery.findResultDoclist - page comparator: " + (pagesize * pagestart) + pagesize);
734
       if(currentIndex >= ((pagesize * pagestart) + pagesize))
735
       {
736
         ResultDocumentSet pagedResultsHash = new ResultDocumentSet();
737
         for(int i=pagesize*pagestart; i<docListResult.size(); i++)
738
         {
739
           pagedResultsHash.put(docListResult.get(i));
740
         }
741
         
742
         docListResult = pagedResultsHash;
743
         break;
744
       }
745
       // Advance to the next record in the cursor
746
       tableHasRows = rs.next();
747
       if(!tableHasRows)
748
       {
749
         ResultDocumentSet pagedResultsHash = new ResultDocumentSet();
750
         //get the last page of information then break
751
         if(pagesize != NONPAGESIZE)
752
         {
753
           for(int i=pagesize*pagestart; i<docListResult.size(); i++)
754
           {
755
             pagedResultsHash.put(docListResult.get(i));
756
           }
757
           docListResult = pagedResultsHash;
758
         }
759
         
760
         lastpage = true;
761
         break;
762
       }
763
     }//while
764
     
765
     rs.close();
766
     pstmt.close();
767
     long docListTime = System.currentTimeMillis() - startSelectionTime;
768
     long docListWarnLimit = Long.parseLong(PropertyService.getProperty("dbquery.findDocListTimeWarnLimit"));
769
     if (docListTime > docListWarnLimit) {
770
    	 logMetacat.warn("DBQuery.findResultDoclist - Total time to get docid list is: "
771
                          + docListTime);
772
     }
773
     MetacatUtil.writeDebugToFile("---------------------------------------------------------------------------------------------------------------Total selection: "
774
             + docListTime);
775
     MetacatUtil.writeDebugToDelimiteredFile(" "+ docListTime, false);
776
     //if docListResult is not empty, it need to be sent.
777
     if (docListResult.size() != 0)
778
     {
779
      
780
       handleSubsetResult(qspec,resultsetBuffer, out, docListResult,
781
                              user, groups,dbconn, useXMLIndex);
782
     }
783

    
784
     resultsetBuffer.append("\n<lastpage>" + lastpage + "</lastpage>\n");
785
     if (out != null)
786
     {
787
         out.println("\n<lastpage>" + lastpage + "</lastpage>\n");
788
     }
789
     
790
     // now we only cached none-paged query and user is public
791
     if (user != null && user.equalsIgnoreCase("public") 
792
    		 && pagesize == NONPAGESIZE && PropertyService.getProperty("database.queryCacheOn").equals("true"))
793
     {
794
       //System.out.println("the string stored into cache is "+ resultsetBuffer.toString());
795
  	   storeQueryResultIntoCache(selectionAndExtendedQuery, resultsetBuffer.toString());
796
     }
797
          
798
     return resultsetBuffer;
799
    }//findReturnDoclist
800

    
801

    
802
    /*
803
     * Send completed search hashtable(part of reulst)to output stream
804
     * and buffer into a buffer stream
805
     */
806
    private StringBuffer handleSubsetResult(QuerySpecification qspec,
807
                                           StringBuffer resultset,
808
                                           PrintWriter out, ResultDocumentSet partOfDoclist,
809
                                           String user, String[]groups,
810
                                       DBConnection dbconn, boolean useXMLIndex)
811
                                       throws Exception
812
   {
813
     double startReturnFieldTime = System.currentTimeMillis();
814
     // check if there is a record in xml_returnfield
815
     // and get the returnfield_id and usage count
816
     int usage_count = getXmlReturnfieldsTableId(qspec, dbconn);
817
     boolean enterRecords = false;
818

    
819
     // get value of database.xmlReturnfieldCount
820
     int count = (new Integer(PropertyService
821
                            .getProperty("database.xmlReturnfieldCount")))
822
                            .intValue();
823

    
824
     // set enterRecords to true if usage_count is more than the offset
825
     // specified in metacat.properties
826
     if(usage_count > count){
827
         enterRecords = true;
828
     }
829

    
830
     if(returnfield_id < 0){
831
         logMetacat.warn("DBQuery.handleSubsetResult - Error in getting returnfield id from"
832
                                  + "xml_returnfield table");
833
         enterRecords = false;
834
     }
835

    
836
     // get the hashtable containing the docids that already in the
837
     // xml_queryresult table
838
     logMetacat.info("DBQuery.handleSubsetResult - size of partOfDoclist before"
839
                             + " docidsInQueryresultTable(): "
840
                             + partOfDoclist.size());
841
     long startGetReturnValueFromQueryresultable = System.currentTimeMillis();
842
     Hashtable queryresultDocList = docidsInQueryresultTable(returnfield_id,
843
                                                        partOfDoclist, dbconn);
844

    
845
     // remove the keys in queryresultDocList from partOfDoclist
846
     Enumeration _keys = queryresultDocList.keys();
847
     while (_keys.hasMoreElements()){
848
         partOfDoclist.remove((String)_keys.nextElement());
849
     }
850
     
851
     long queryResultReturnValuetime = System.currentTimeMillis() - startGetReturnValueFromQueryresultable;
852
     long queryResultWarnLimit = 
853
    	 Long.parseLong(PropertyService.getProperty("dbquery.findQueryResultsTimeWarnLimit"));
854
     
855
     if (queryResultReturnValuetime > queryResultWarnLimit) {
856
    	 logMetacat.warn("DBQuery.handleSubsetResult - Time to get return fields from xml_queryresult table is (Part1 in return fields) " +
857
    		 queryResultReturnValuetime);
858
     }
859
     MetacatUtil.writeDebugToFile("-----------------------------------------Get fields from xml_queryresult(Part1 in return fields) " +
860
    		 queryResultReturnValuetime);
861
     MetacatUtil.writeDebugToDelimiteredFile(" " + queryResultReturnValuetime,false);
862
     
863
     long startExtendedQuery = System.currentTimeMillis();
864
     // backup the keys-elements in partOfDoclist to check later
865
     // if the doc entry is indexed yet
866
     Hashtable partOfDoclistBackup = new Hashtable();
867
     Iterator itt = partOfDoclist.getDocids();
868
     while (itt.hasNext()){
869
       Object key = itt.next();
870
         partOfDoclistBackup.put(key, partOfDoclist.get(key));
871
     }
872

    
873
     logMetacat.info("DBQuery.handleSubsetResult - size of partOfDoclist after"
874
                             + " docidsInQueryresultTable(): "
875
                             + partOfDoclist.size());
876

    
877
     //add return fields for the documents in partOfDoclist
878
     partOfDoclist = addReturnfield(partOfDoclist, qspec, user, groups,
879
                                        dbconn, useXMLIndex);
880
     long extendedQueryRunTime = startExtendedQuery - System.currentTimeMillis();
881
     long extendedQueryWarnLimit = 
882
    	 Long.parseLong(PropertyService.getProperty("dbquery.extendedQueryRunTimeWarnLimit"));
883
  
884
     if (extendedQueryRunTime > extendedQueryWarnLimit) {
885
    	 logMetacat.warn("DBQuery.handleSubsetResult - Get fields from index and node table (Part2 in return fields) "
886
        		                                          + extendedQueryRunTime);
887
     }
888
     MetacatUtil.writeDebugToFile("-----------------------------------------Get fields from extened query(Part2 in return fields) "
889
             + extendedQueryRunTime);
890
     MetacatUtil.writeDebugToDelimiteredFile(" "
891
             + extendedQueryRunTime, false);
892
     //add relationship part part docid list for the documents in partOfDocList
893
     //partOfDoclist = addRelationship(partOfDoclist, qspec, dbconn, useXMLIndex);
894

    
895
     long startStoreReturnField = System.currentTimeMillis();
896
     Iterator keys = partOfDoclist.getDocids();
897
     String key = null;
898
     String element = null;
899
     String query = null;
900
     int offset = (new Integer(PropertyService
901
                               .getProperty("database.queryresultStringLength")))
902
                               .intValue();
903
     while (keys.hasNext())
904
     {
905
         key = (String) keys.next();
906
         element = (String)partOfDoclist.get(key);
907
         
908
	 // check if the enterRecords is true, elements is not null, element's
909
         // length is less than the limit of table column and if the document
910
         // has been indexed already
911
         if(enterRecords && element != null
912
		&& element.length() < offset
913
		&& element.compareTo((String) partOfDoclistBackup.get(key)) != 0){
914
             query = "INSERT INTO xml_queryresult (returnfield_id, docid, "
915
                 + "queryresult_string) VALUES (?, ?, ?)";
916

    
917
             PreparedStatement pstmt = null;
918
             pstmt = dbconn.prepareStatement(query);
919
             pstmt.setInt(1, returnfield_id);
920
             pstmt.setString(2, key);
921
             pstmt.setString(3, element);
922
            
923
             dbconn.increaseUsageCount(1);
924
             try
925
             {
926
            	 pstmt.execute();
927
             }
928
             catch(Exception e)
929
             {
930
            	 logMetacat.warn("DBQuery.handleSubsetResult - couldn't insert the element to xml_queryresult table "+e.getLocalizedMessage());
931
             }
932
             finally
933
             {
934
                pstmt.close();
935
             }
936
         }
937
        
938
         // A string with element
939
         String xmlElement = "  <document>" + element + "</document>";
940

    
941
         //send single element to output
942
         if (out != null)
943
         {
944
             out.println(xmlElement);
945
         }
946
         resultset.append(xmlElement);
947
     }//while
948
     
949
     double storeReturnFieldTime = System.currentTimeMillis() - startStoreReturnField;
950
     long storeReturnFieldWarnLimit = 
951
    	 Long.parseLong(PropertyService.getProperty("dbquery.storeReturnFieldTimeWarnLimit"));
952

    
953
     if (storeReturnFieldTime > storeReturnFieldWarnLimit) {
954
    	 logMetacat.warn("DBQuery.handleSubsetResult - Time to store new return fields into xml_queryresult table (Part4 in return fields) "
955
                   + storeReturnFieldTime);
956
     }
957
     MetacatUtil.writeDebugToFile("-----------------------------------------Insert new record to xml_queryresult(Part4 in return fields) "
958
             + storeReturnFieldTime);
959
     MetacatUtil.writeDebugToDelimiteredFile(" " + storeReturnFieldTime, false);
960
     
961
     Enumeration keysE = queryresultDocList.keys();
962
     while (keysE.hasMoreElements())
963
     {
964
         key = (String) keysE.nextElement();
965
         element = (String)queryresultDocList.get(key);
966
         // A string with element
967
         String xmlElement = "  <document>" + element + "</document>";
968
         //send single element to output
969
         if (out != null)
970
         {
971
             out.println(xmlElement);
972
         }
973
         resultset.append(xmlElement);
974
     }//while
975
     double returnFieldTime = System.currentTimeMillis() - startReturnFieldTime;
976
     long totalReturnFieldWarnLimit = 
977
    	 Long.parseLong(PropertyService.getProperty("dbquery.totalReturnFieldTimeWarnLimit"));
978

    
979
     if (returnFieldTime > totalReturnFieldWarnLimit) {
980
    	 logMetacat.warn("DBQuery.handleSubsetResult - Total time to get return fields is: "
981
                           + returnFieldTime);
982
     }
983
     MetacatUtil.writeDebugToFile("DBQuery.handleSubsetResult - ---------------------------------------------------------------------------------------------------------------"+
984
    		 "Total to get return fields  " + returnFieldTime);
985
     MetacatUtil.writeDebugToDelimiteredFile("DBQuery.handleSubsetResult - "+ returnFieldTime, false);
986
     return resultset;
987
 }
988

    
989
   /**
990
    * Get the docids already in xml_queryresult table and corresponding
991
    * queryresultstring as a hashtable
992
    */
993
   private Hashtable docidsInQueryresultTable(int returnfield_id,
994
                                              ResultDocumentSet partOfDoclist,
995
                                              DBConnection dbconn){
996

    
997
         Hashtable returnValue = new Hashtable();
998
         PreparedStatement pstmt = null;
999
         ResultSet rs = null;
1000

    
1001
         // get partOfDoclist as string for the query
1002
         Iterator keylist = partOfDoclist.getDocids();
1003
         StringBuffer doclist = new StringBuffer();
1004
         while (keylist.hasNext())
1005
         {
1006
             doclist.append("'");
1007
             doclist.append((String) keylist.next());
1008
             doclist.append("',");
1009
         }//while
1010

    
1011

    
1012
         if (doclist.length() > 0)
1013
         {
1014
             doclist.deleteCharAt(doclist.length() - 1); //remove the last comma
1015

    
1016
             // the query to find out docids from xml_queryresult
1017
             String query = "select docid, queryresult_string from "
1018
                          + "xml_queryresult where returnfield_id = " +
1019
                          returnfield_id +" and docid in ("+ doclist + ")";
1020
             logMetacat.info("DBQuery.docidsInQueryresultTable - Query to get docids from xml_queryresult:"
1021
                                      + query);
1022

    
1023
             try {
1024
                 // prepare and execute the query
1025
                 pstmt = dbconn.prepareStatement(query);
1026
                 dbconn.increaseUsageCount(1);
1027
                 pstmt.execute();
1028
                 rs = pstmt.getResultSet();
1029
                 boolean tableHasRows = rs.next();
1030
                 while (tableHasRows) {
1031
                     // store the returned results in the returnValue hashtable
1032
                     String key = rs.getString(1);
1033
                     String element = rs.getString(2);
1034

    
1035
                     if(element != null){
1036
                         returnValue.put(key, element);
1037
                     } else {
1038
                         logMetacat.info("DBQuery.docidsInQueryresultTable - Null elment found ("
1039
                         + "DBQuery.docidsInQueryresultTable)");
1040
                     }
1041
                     tableHasRows = rs.next();
1042
                 }
1043
                 rs.close();
1044
                 pstmt.close();
1045
             } catch (Exception e){
1046
                 logMetacat.error("DBQuery.docidsInQueryresultTable - Error getting docids from "
1047
                                          + "queryresult: " + e.getMessage());
1048
              }
1049
         }
1050
         return returnValue;
1051
     }
1052

    
1053

    
1054
   /**
1055
    * Method to get id from xml_returnfield table
1056
    * for a given query specification
1057
    */
1058
   private int returnfield_id;
1059
   private int getXmlReturnfieldsTableId(QuerySpecification qspec,
1060
                                           DBConnection dbconn){
1061
       int id = -1;
1062
       int count = 1;
1063
       PreparedStatement pstmt = null;
1064
       ResultSet rs = null;
1065
       String returnfield = qspec.getSortedReturnFieldString();
1066

    
1067
       // query for finding the id from xml_returnfield
1068
       String query = "SELECT returnfield_id, usage_count FROM xml_returnfield "
1069
            + "WHERE returnfield_string LIKE ?";
1070
       logMetacat.info("DBQuery.getXmlReturnfieldsTableId - ReturnField Query:" + query);
1071

    
1072
       try {
1073
           // prepare and run the query
1074
           pstmt = dbconn.prepareStatement(query);
1075
           pstmt.setString(1,returnfield);
1076
           dbconn.increaseUsageCount(1);
1077
           pstmt.execute();
1078
           rs = pstmt.getResultSet();
1079
           boolean tableHasRows = rs.next();
1080

    
1081
           // if record found then increase the usage count
1082
           // else insert a new record and get the id of the new record
1083
           if(tableHasRows){
1084
               // get the id
1085
               id = rs.getInt(1);
1086
               count = rs.getInt(2) + 1;
1087
               rs.close();
1088
               pstmt.close();
1089

    
1090
               // increase the usage count
1091
               query = "UPDATE xml_returnfield SET usage_count ='" + count
1092
                   + "' WHERE returnfield_id ='"+ id +"'";
1093
               logMetacat.info("DBQuery.getXmlReturnfieldsTableId - ReturnField Table Update:"+ query);
1094

    
1095
               pstmt = dbconn.prepareStatement(query);
1096
               dbconn.increaseUsageCount(1);
1097
               pstmt.execute();
1098
               pstmt.close();
1099

    
1100
           } else {
1101
               rs.close();
1102
               pstmt.close();
1103

    
1104
               // insert a new record
1105
               query = "INSERT INTO xml_returnfield (returnfield_string, usage_count)"
1106
                   + "VALUES (?, '1')";
1107
               logMetacat.info("DBQuery.getXmlReturnfieldsTableId - ReturnField Table Insert:"+ query);
1108
               pstmt = dbconn.prepareStatement(query);
1109
               pstmt.setString(1, returnfield);
1110
               dbconn.increaseUsageCount(1);
1111
               pstmt.execute();
1112
               pstmt.close();
1113

    
1114
               // get the id of the new record
1115
               query = "SELECT returnfield_id FROM xml_returnfield "
1116
                   + "WHERE returnfield_string LIKE ?";
1117
               logMetacat.info("DBQuery.getXmlReturnfieldsTableId - ReturnField query after Insert:" + query);
1118
               pstmt = dbconn.prepareStatement(query);
1119
               pstmt.setString(1, returnfield);
1120

    
1121
               dbconn.increaseUsageCount(1);
1122
               pstmt.execute();
1123
               rs = pstmt.getResultSet();
1124
               if(rs.next()){
1125
                   id = rs.getInt(1);
1126
               } else {
1127
                   id = -1;
1128
               }
1129
               rs.close();
1130
               pstmt.close();
1131
           }
1132

    
1133
       } catch (Exception e){
1134
           logMetacat.error("DBQuery.getXmlReturnfieldsTableId - Error getting id from xml_returnfield in "
1135
                                     + "DBQuery.getXmlReturnfieldsTableId: "
1136
                                     + e.getMessage());
1137
           id = -1;
1138
       }
1139

    
1140
       returnfield_id = id;
1141
       return count;
1142
   }
1143

    
1144

    
1145
    /*
1146
     * A method to add return field to return doclist hash table
1147
     */
1148
    private ResultDocumentSet addReturnfield(ResultDocumentSet docListResult,
1149
                                      QuerySpecification qspec,
1150
                                      String user, String[]groups,
1151
                                      DBConnection dbconn, boolean useXMLIndex )
1152
                                      throws Exception
1153
    {
1154
      PreparedStatement pstmt = null;
1155
      ResultSet rs = null;
1156
      String docid = null;
1157
      String fieldname = null;
1158
      String fieldtype = null;
1159
      String fielddata = null;
1160
      String relation = null;
1161

    
1162
      if (qspec.containsExtendedSQL())
1163
      {
1164
        qspec.setUserName(user);
1165
        qspec.setGroup(groups);
1166
        Vector extendedFields = new Vector(qspec.getReturnFieldList());
1167
        Vector results = new Vector();
1168
        Iterator keylist = docListResult.getDocids();
1169
        StringBuffer doclist = new StringBuffer();
1170
        Vector parentidList = new Vector();
1171
        Hashtable returnFieldValue = new Hashtable();
1172
        while (keylist.hasNext())
1173
        {
1174
          doclist.append("'");
1175
          doclist.append((String) keylist.next());
1176
          doclist.append("',");
1177
        }
1178
        if (doclist.length() > 0)
1179
        {
1180
          Hashtable controlPairs = new Hashtable();
1181
          doclist.deleteCharAt(doclist.length() - 1); //remove the last comma
1182
          boolean tableHasRows = false;
1183
        
1184

    
1185
           String extendedQuery =
1186
               qspec.printExtendedSQL(doclist.toString(), useXMLIndex);
1187
           logMetacat.info("DBQuery.addReturnfield - Extended query: " + extendedQuery);
1188

    
1189
           if(extendedQuery != null){
1190
//        	   long extendedQueryStart = System.currentTimeMillis();
1191
               pstmt = dbconn.prepareStatement(extendedQuery);
1192
               //increase dbconnection usage count
1193
               dbconn.increaseUsageCount(1);
1194
               pstmt.execute();
1195
               rs = pstmt.getResultSet();
1196
//               MetacatUtil.writeDebugToDelimiteredFile(" "+ (extendedQueryEnd - extendedQueryStart), false);
1197
               tableHasRows = rs.next();
1198
               while (tableHasRows) {
1199
                   ReturnFieldValue returnValue = new ReturnFieldValue();
1200
                   docid = rs.getString(1).trim();
1201
                   fieldname = rs.getString(2);
1202
                   fielddata = rs.getString(3);
1203
                   fielddata = MetacatUtil.normalize(fielddata);
1204
                   String parentId = rs.getString(4);
1205
                   fieldtype = rs.getString(5);
1206
                   StringBuffer value = new StringBuffer();
1207

    
1208
                   //handle case when usexmlindex is true differently
1209
                   //at one point merging the nodedata (for large text elements) was 
1210
                   //deemed unnecessary - but now it is needed.  but not for attribute nodes
1211
                   if (useXMLIndex || !containsKey(parentidList, parentId)) {
1212
                	   //merge node data only for non-ATTRIBUTEs
1213
                	   if (fieldtype != null && !fieldtype.equals("ATTRIBUTE")) {
1214
	                	   //try merging the data
1215
	                	   ReturnFieldValue existingRFV =
1216
	                		   getArrayValue(parentidList, parentId);
1217
	                	   if (existingRFV != null) {
1218
	                		   fielddata = existingRFV.getFieldValue() + fielddata;
1219
	                	   }
1220
                	   }
1221
                       value.append("<param name=\"");
1222
                       value.append(fieldname);
1223
                       value.append("\">");
1224
                       value.append(fielddata);
1225
                       value.append("</param>");
1226
                       //set returnvalue
1227
                       returnValue.setDocid(docid);
1228
                       returnValue.setFieldValue(fielddata);
1229
                       returnValue.setFieldType(fieldtype);
1230
                       returnValue.setXMLFieldValue(value.toString());
1231
                       // Store it in hastable
1232
                       putInArray(parentidList, parentId, returnValue);
1233
                   }
1234
                   else {
1235
                       // need to merge nodedata if they have same parent id and
1236
                       // node type is text
1237
                       fielddata = (String) ( (ReturnFieldValue)
1238
                                             getArrayValue(
1239
                           parentidList, parentId)).getFieldValue()
1240
                           + fielddata;
1241
                       value.append("<param name=\"");
1242
                       value.append(fieldname);
1243
                       value.append("\">");
1244
                       value.append(fielddata);
1245
                       value.append("</param>");
1246
                       returnValue.setDocid(docid);
1247
                       returnValue.setFieldValue(fielddata);
1248
                       returnValue.setFieldType(fieldtype);
1249
                       returnValue.setXMLFieldValue(value.toString());
1250
                       // remove the old return value from paretnidList
1251
                       parentidList.remove(parentId);
1252
                       // store the new return value in parentidlit
1253
                       putInArray(parentidList, parentId, returnValue);
1254
                   }
1255
                   tableHasRows = rs.next();
1256
               } //while
1257
               rs.close();
1258
               pstmt.close();
1259

    
1260
               // put the merger node data info into doclistReult
1261
               Enumeration xmlFieldValue = (getElements(parentidList)).
1262
                   elements();
1263
               while (xmlFieldValue.hasMoreElements()) {
1264
                   ReturnFieldValue object =
1265
                       (ReturnFieldValue) xmlFieldValue.nextElement();
1266
                   docid = object.getDocid();
1267
                   if (docListResult.containsDocid(docid)) {
1268
                       String removedelement = (String) docListResult.
1269
                           remove(docid);
1270
                       docListResult.
1271
                           addResultDocument(new ResultDocument(docid,
1272
                               removedelement + object.getXMLFieldValue()));
1273
                   }
1274
                   else {
1275
                       docListResult.addResultDocument(
1276
                         new ResultDocument(docid, object.getXMLFieldValue()));
1277
                   }
1278
               } //while
1279
//               double docListResultEnd = System.currentTimeMillis() / 1000;
1280
//               logMetacat.warn(
1281
//                   "Time to prepare ResultDocumentSet after"
1282
//                   + " execute extended query: "
1283
//                   + (docListResultEnd - extendedQueryEnd));
1284
           }
1285

    
1286
         
1287
           
1288
           
1289
       }//if doclist lenght is great than zero
1290

    
1291
     }//if has extended query
1292

    
1293
      return docListResult;
1294
    }//addReturnfield
1295

    
1296
  
1297
  /**
1298
   * removes the <?xml version="1.0"?> tag from the beginning.  This takes a
1299
   * string as a param instead of a hashtable.
1300
   *
1301
   * @param xmlquery a string representing a query.
1302
   */
1303
   private  String transformQuery(String xmlquery)
1304
   {
1305
     xmlquery = xmlquery.trim();
1306
     int index = xmlquery.indexOf("?>");
1307
     if (index != -1)
1308
     {
1309
       return xmlquery.substring(index + 2, xmlquery.length());
1310
     }
1311
     else
1312
     {
1313
       return xmlquery;
1314
     }
1315
   }
1316
   
1317
   /*
1318
    * Method to store query string and result xml string into query result
1319
    * cache. If the size alreay reache the limitation, the cache will be
1320
    * cleared first, then store them.
1321
    */
1322
   private void storeQueryResultIntoCache(String query, String resultXML)
1323
   {
1324
	   synchronized (queryResultCache)
1325
	   {
1326
		   if (queryResultCache.size() >= QUERYRESULTCACHESIZE)
1327
		   {
1328
			   queryResultCache.clear();
1329
		   }
1330
		   queryResultCache.put(query, resultXML);
1331
		   
1332
	   }
1333
   }
1334
   
1335
   /*
1336
    * Method to get result xml string from query result cache. 
1337
    * Note: the returned string can be null.
1338
    */
1339
   private String getResultXMLFromCache(String query)
1340
   {
1341
	   String resultSet = null;
1342
	   synchronized (queryResultCache)
1343
	   {
1344
          try
1345
          {
1346
        	 logMetacat.info("DBQuery.getResultXMLFromCache - Get query from cache");
1347
		     resultSet = (String)queryResultCache.get(query);
1348
		   
1349
          }
1350
          catch (Exception e)
1351
          {
1352
        	  resultSet = null;
1353
          }
1354
		   
1355
	   }
1356
	   return resultSet;
1357
   }
1358
   
1359
   /**
1360
    * Method to clear the query result cache.
1361
    */
1362
   public static void clearQueryResultCache()
1363
   {
1364
	   synchronized (queryResultCache)
1365
	   {
1366
		   queryResultCache.clear();
1367
	   }
1368
   }
1369

    
1370

    
1371
    /*
1372
     * A method to search if Vector contains a particular key string
1373
     */
1374
    private boolean containsKey(Vector parentidList, String parentId)
1375
    {
1376

    
1377
        Vector tempVector = null;
1378

    
1379
        for (int count = 0; count < parentidList.size(); count++) {
1380
            tempVector = (Vector) parentidList.get(count);
1381
            if (parentId.compareTo((String) tempVector.get(0)) == 0) { return true; }
1382
        }
1383
        return false;
1384
    }
1385
    
1386
    /*
1387
     * A method to put key and value in Vector
1388
     */
1389
    private void putInArray(Vector parentidList, String key,
1390
            ReturnFieldValue value)
1391
    {
1392

    
1393
        Vector tempVector = null;
1394
        //only filter if the field type is NOT an attribute (say, for text)
1395
        String fieldType = value.getFieldType();
1396
        if (fieldType != null && !fieldType.equals("ATTRIBUTE")) {
1397
        
1398
	        for (int count = 0; count < parentidList.size(); count++) {
1399
	            tempVector = (Vector) parentidList.get(count);
1400
	
1401
	            if (key.compareTo((String) tempVector.get(0)) == 0) {
1402
	                tempVector.remove(1);
1403
	                tempVector.add(1, value);
1404
	                return;
1405
	            }
1406
	        }
1407
        }
1408

    
1409
        tempVector = new Vector();
1410
        tempVector.add(0, key);
1411
        tempVector.add(1, value);
1412
        parentidList.add(tempVector);
1413
        return;
1414
    }
1415

    
1416
    /*
1417
     * A method to get value in Vector given a key
1418
     */
1419
    private ReturnFieldValue getArrayValue(Vector parentidList, String key)
1420
    {
1421

    
1422
        Vector tempVector = null;
1423

    
1424
        for (int count = 0; count < parentidList.size(); count++) {
1425
            tempVector = (Vector) parentidList.get(count);
1426

    
1427
            if (key.compareTo((String) tempVector.get(0)) == 0) { return (ReturnFieldValue) tempVector
1428
                    .get(1); }
1429
        }
1430
        return null;
1431
    }
1432

    
1433
    /*
1434
     * A method to get enumeration of all values in Vector
1435
     */
1436
    private Vector getElements(Vector parentidList)
1437
    {
1438
        Vector enumVector = new Vector();
1439
        Vector tempVector = null;
1440

    
1441
        for (int count = 0; count < parentidList.size(); count++) {
1442
            tempVector = (Vector) parentidList.get(count);
1443

    
1444
            enumVector.add(tempVector.get(1));
1445
        }
1446
        return enumVector;
1447
    }
1448

    
1449
  
1450

    
1451
    /*
1452
     * A method to create a query to get owner's docid list
1453
     */
1454
    private String getOwnerQuery(String owner)
1455
    {
1456
        if (owner != null) {
1457
            owner = owner.toLowerCase();
1458
        }
1459
        StringBuffer self = new StringBuffer();
1460

    
1461
        self.append("SELECT docid,docname,doctype,");
1462
        self.append("date_created, date_updated, rev ");
1463
        self.append("FROM xml_documents WHERE docid IN (");
1464
        self.append("(");
1465
        self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n");
1466
        self.append("nodedata LIKE '%%%' ");
1467
        self.append(") \n");
1468
        self.append(") ");
1469
        self.append(" AND (");
1470
        self.append(" lower(user_owner) = '" + owner + "'");
1471
        self.append(") ");
1472
        return self.toString();
1473
    }
1474

    
1475
    /**
1476
     * format a structured query as an XML document that conforms to the
1477
     * pathquery.dtd and is appropriate for submission to the DBQuery
1478
     * structured query engine
1479
     *
1480
     * @param params The list of parameters that should be included in the
1481
     *            query
1482
     */
1483
    public static String createSQuery(Hashtable params) throws PropertyNotFoundException
1484
    {
1485
        StringBuffer query = new StringBuffer();
1486
        Enumeration elements;
1487
        Enumeration keys;
1488
        String filterDoctype = null;
1489
        String casesensitive = null;
1490
        String searchmode = null;
1491
        Object nextkey;
1492
        Object nextelement;
1493
        //add the xml headers
1494
        query.append("<?xml version=\"1.0\"?>\n");
1495
        query.append("<pathquery version=\"1.2\">\n");
1496

    
1497

    
1498

    
1499
        if (params.containsKey("meta_file_id")) {
1500
            query.append("<meta_file_id>");
1501
            query.append(((String[]) params.get("meta_file_id"))[0]);
1502
            query.append("</meta_file_id>");
1503
        }
1504

    
1505
        if (params.containsKey("returndoctype")) {
1506
            String[] returnDoctypes = ((String[]) params.get("returndoctype"));
1507
            for (int i = 0; i < returnDoctypes.length; i++) {
1508
                String doctype = (String) returnDoctypes[i];
1509

    
1510
                if (!doctype.equals("any") && !doctype.equals("ANY")
1511
                        && !doctype.equals("")) {
1512
                    query.append("<returndoctype>").append(doctype);
1513
                    query.append("</returndoctype>");
1514
                }
1515
            }
1516
        }
1517

    
1518
        if (params.containsKey("filterdoctype")) {
1519
            String[] filterDoctypes = ((String[]) params.get("filterdoctype"));
1520
            for (int i = 0; i < filterDoctypes.length; i++) {
1521
                query.append("<filterdoctype>").append(filterDoctypes[i]);
1522
                query.append("</filterdoctype>");
1523
            }
1524
        }
1525

    
1526
        if (params.containsKey("returnfield")) {
1527
            String[] returnfield = ((String[]) params.get("returnfield"));
1528
            for (int i = 0; i < returnfield.length; i++) {
1529
                query.append("<returnfield>").append(returnfield[i]);
1530
                query.append("</returnfield>");
1531
            }
1532
        }
1533

    
1534
        if (params.containsKey("owner")) {
1535
            String[] owner = ((String[]) params.get("owner"));
1536
            for (int i = 0; i < owner.length; i++) {
1537
                query.append("<owner>").append(owner[i]);
1538
                query.append("</owner>");
1539
            }
1540
        }
1541

    
1542
        if (params.containsKey("site")) {
1543
            String[] site = ((String[]) params.get("site"));
1544
            for (int i = 0; i < site.length; i++) {
1545
                query.append("<site>").append(site[i]);
1546
                query.append("</site>");
1547
            }
1548
        }
1549

    
1550
        //allows the dynamic switching of boolean operators
1551
        if (params.containsKey("operator")) {
1552
            query.append("<querygroup operator=\""
1553
                    + ((String[]) params.get("operator"))[0] + "\">");
1554
        } else { //the default operator is UNION
1555
            query.append("<querygroup operator=\"UNION\">");
1556
        }
1557

    
1558
        if (params.containsKey("casesensitive")) {
1559
            casesensitive = ((String[]) params.get("casesensitive"))[0];
1560
        } else {
1561
            casesensitive = "false";
1562
        }
1563

    
1564
        if (params.containsKey("searchmode")) {
1565
            searchmode = ((String[]) params.get("searchmode"))[0];
1566
        } else {
1567
            searchmode = "contains";
1568
        }
1569

    
1570
        //anyfield is a special case because it does a
1571
        //free text search. It does not have a <pathexpr>
1572
        //tag. This allows for a free text search within the structured
1573
        //query. This is useful if the INTERSECT operator is used.
1574
        if (params.containsKey("anyfield")) {
1575
            String[] anyfield = ((String[]) params.get("anyfield"));
1576
            //allow for more than one value for anyfield
1577
            for (int i = 0; i < anyfield.length; i++) {
1578
                if (anyfield[i] != null && !anyfield[i].equals("")) {
1579
                    query.append("<queryterm casesensitive=\"" + casesensitive
1580
                            + "\" " + "searchmode=\"" + searchmode
1581
                            + "\"><value>" + anyfield[i]
1582
                            + "</value></queryterm>");
1583
                }
1584
            }
1585
        }
1586

    
1587
        //this while loop finds the rest of the parameters
1588
        //and attempts to query for the field specified
1589
        //by the parameter.
1590
        elements = params.elements();
1591
        keys = params.keys();
1592
        while (keys.hasMoreElements() && elements.hasMoreElements()) {
1593
            nextkey = keys.nextElement();
1594
            nextelement = elements.nextElement();
1595

    
1596
            //make sure we aren't querying for any of these
1597
            //parameters since the are already in the query
1598
            //in one form or another.
1599
            Vector ignoredParams = new Vector();
1600
            ignoredParams.add("returndoctype");
1601
            ignoredParams.add("filterdoctype");
1602
            ignoredParams.add("action");
1603
            ignoredParams.add("qformat");
1604
            ignoredParams.add("anyfield");
1605
            ignoredParams.add("returnfield");
1606
            ignoredParams.add("owner");
1607
            ignoredParams.add("site");
1608
            ignoredParams.add("operator");
1609
            ignoredParams.add("sessionid");
1610
            ignoredParams.add("pagesize");
1611
            ignoredParams.add("pagestart");
1612
            ignoredParams.add("searchmode");
1613

    
1614
            // Also ignore parameters listed in the properties file
1615
            // so that they can be passed through to stylesheets
1616
            String paramsToIgnore = PropertyService
1617
                    .getProperty("database.queryignoredparams");
1618
            StringTokenizer st = new StringTokenizer(paramsToIgnore, ",");
1619
            while (st.hasMoreTokens()) {
1620
                ignoredParams.add(st.nextToken());
1621
            }
1622
            if (!ignoredParams.contains(nextkey.toString())) {
1623
                //allow for more than value per field name
1624
                for (int i = 0; i < ((String[]) nextelement).length; i++) {
1625
                    if (!((String[]) nextelement)[i].equals("")) {
1626
                        query.append("<queryterm casesensitive=\""
1627
                                + casesensitive + "\" " + "searchmode=\""
1628
                                + searchmode + "\">" + "<value>" +
1629
                                //add the query value
1630
                                ((String[]) nextelement)[i]
1631
                                + "</value><pathexpr>" +
1632
                                //add the path to query by
1633
                                nextkey.toString() + "</pathexpr></queryterm>");
1634
                    }
1635
                }
1636
            }
1637
        }
1638
        query.append("</querygroup></pathquery>");
1639
        //append on the end of the xml and return the result as a string
1640
        return query.toString();
1641
    }
1642

    
1643
    /**
1644
     * format a simple free-text value query as an XML document that conforms
1645
     * to the pathquery.dtd and is appropriate for submission to the DBQuery
1646
     * structured query engine
1647
     *
1648
     * @param value the text string to search for in the xml catalog
1649
     * @param doctype the type of documents to include in the result set -- use
1650
     *            "any" or "ANY" for unfiltered result sets
1651
     */
1652
    public static String createQuery(String value, String doctype)
1653
    {
1654
        StringBuffer xmlquery = new StringBuffer();
1655
        xmlquery.append("<?xml version=\"1.0\"?>\n");
1656
        xmlquery.append("<pathquery version=\"1.0\">");
1657

    
1658
        if (!doctype.equals("any") && !doctype.equals("ANY")) {
1659
            xmlquery.append("<returndoctype>");
1660
            xmlquery.append(doctype).append("</returndoctype>");
1661
        }
1662

    
1663
        xmlquery.append("<querygroup operator=\"UNION\">");
1664
        //chad added - 8/14
1665
        //the if statement allows a query to gracefully handle a null
1666
        //query. Without this if a nullpointerException is thrown.
1667
        if (!value.equals("")) {
1668
            xmlquery.append("<queryterm casesensitive=\"false\" ");
1669
            xmlquery.append("searchmode=\"contains\">");
1670
            xmlquery.append("<value>").append(value).append("</value>");
1671
            xmlquery.append("</queryterm>");
1672
        }
1673
        xmlquery.append("</querygroup>");
1674
        xmlquery.append("</pathquery>");
1675

    
1676
        return (xmlquery.toString());
1677
    }
1678

    
1679
    /**
1680
     * format a simple free-text value query as an XML document that conforms
1681
     * to the pathquery.dtd and is appropriate for submission to the DBQuery
1682
     * structured query engine
1683
     *
1684
     * @param value the text string to search for in the xml catalog
1685
     */
1686
    public static String createQuery(String value)
1687
    {
1688
        return createQuery(value, "any");
1689
    }
1690

    
1691
    /**
1692
     * Check for "READ" permission on @docid for @user and/or @group from DB
1693
     * connection
1694
     */
1695
    private boolean hasPermission(String user, String[] groups, String docid)
1696
            throws SQLException, Exception
1697
    {
1698
        // Check for READ permission on @docid for @user and/or @groups
1699
        PermissionController controller = new PermissionController(docid);
1700
        return controller.hasPermission(user, groups,
1701
                AccessControlInterface.READSTRING);
1702
    }
1703

    
1704
    /**
1705
     * Get all docIds list for a data packadge
1706
     *
1707
     * @param dataPackageDocid, the string in docId field of xml_relation table
1708
     */
1709
    private Vector getCurrentDocidListForDataPackage(String dataPackageDocid)
1710
    {
1711
        DBConnection dbConn = null;
1712
        int serialNumber = -1;
1713
        Vector docIdList = new Vector();//return value
1714
        PreparedStatement pStmt = null;
1715
        ResultSet rs = null;
1716
        String docIdInSubjectField = null;
1717
        String docIdInObjectField = null;
1718

    
1719
        // Check the parameter
1720
        if (dataPackageDocid == null || dataPackageDocid.equals("")) { return docIdList; }//if
1721

    
1722
        //the query stirng
1723
        String query = "SELECT subject, object from xml_relation where docId = ?";
1724
        try {
1725
            dbConn = DBConnectionPool
1726
                    .getDBConnection("DBQuery.getCurrentDocidListForDataPackage");
1727
            serialNumber = dbConn.getCheckOutSerialNumber();
1728
            pStmt = dbConn.prepareStatement(query);
1729
            //bind the value to query
1730
            pStmt.setString(1, dataPackageDocid);
1731

    
1732
            //excute the query
1733
            pStmt.execute();
1734
            //get the result set
1735
            rs = pStmt.getResultSet();
1736
            //process the result
1737
            while (rs.next()) {
1738
                //In order to get the whole docIds in a data packadge,
1739
                //we need to put the docIds of subject and object field in
1740
                // xml_relation
1741
                //into the return vector
1742
                docIdInSubjectField = rs.getString(1);//the result docId in
1743
                                                      // subject field
1744
                docIdInObjectField = rs.getString(2);//the result docId in
1745
                                                     // object field
1746

    
1747
                //don't put the duplicate docId into the vector
1748
                if (!docIdList.contains(docIdInSubjectField)) {
1749
                    docIdList.add(docIdInSubjectField);
1750
                }
1751

    
1752
                //don't put the duplicate docId into the vector
1753
                if (!docIdList.contains(docIdInObjectField)) {
1754
                    docIdList.add(docIdInObjectField);
1755
                }
1756
            }//while
1757
            //close the pStmt
1758
            pStmt.close();
1759
        }//try
1760
        catch (SQLException e) {
1761
            logMetacat.error("DBQuery.getCurrentDocidListForDataPackage - Error in getDocidListForDataPackage: "
1762
                    + e.getMessage());
1763
        }//catch
1764
        finally {
1765
            try {
1766
                pStmt.close();
1767
            }//try
1768
            catch (SQLException ee) {
1769
                logMetacat.error("DBQuery.getCurrentDocidListForDataPackage - SQL Error: "
1770
                                + ee.getMessage());
1771
            }//catch
1772
            finally {
1773
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1774
            }//fianlly
1775
        }//finally
1776
        return docIdList;
1777
    }//getCurrentDocidListForDataPackadge()
1778

    
1779
    /**
1780
     * Get all docIds list for a data packadge
1781
     *
1782
     * @param dataPackageDocid, the string in docId field of xml_relation table
1783
     */
1784
    private Vector getOldVersionDocidListForDataPackage(String dataPackageDocidWithRev)
1785
    {
1786

    
1787
        Vector docIdList = new Vector();//return value
1788
        Vector tripleList = null;
1789
        String xml = null;
1790

    
1791
        // Check the parameter
1792
        if (dataPackageDocidWithRev == null || dataPackageDocidWithRev.equals("")) { return docIdList; }//if
1793

    
1794
        try {
1795
            //initial a documentImpl object
1796
            DocumentImpl packageDocument = new DocumentImpl(dataPackageDocidWithRev);
1797
            //transfer to documentImpl object to string
1798
            xml = packageDocument.toString();
1799

    
1800
            //create a tripcollection object
1801
            TripleCollection tripleForPackage = new TripleCollection(
1802
                    new StringReader(xml));
1803
            //get the vetor of triples
1804
            tripleList = tripleForPackage.getCollection();
1805

    
1806
            for (int i = 0; i < tripleList.size(); i++) {
1807
                //put subject docid into docIdlist without duplicate
1808
                if (!docIdList.contains(((Triple) tripleList.elementAt(i))
1809
                        .getSubject())) {
1810
                    //put subject docid into docIdlist
1811
                    docIdList.add(((Triple) tripleList.get(i)).getSubject());
1812
                }
1813
                //put object docid into docIdlist without duplicate
1814
                if (!docIdList.contains(((Triple) tripleList.elementAt(i))
1815
                        .getObject())) {
1816
                    docIdList.add(((Triple) (tripleList.get(i))).getObject());
1817
                }
1818
            }//for
1819
        }//try
1820
        catch (Exception e) {
1821
            logMetacat.error("DBQuery.getCurrentDocidListForDataPackage - General error: "
1822
                    + e.getMessage());
1823
        }//catch
1824

    
1825
        // return result
1826
        return docIdList;
1827
    }//getDocidListForPackageInXMLRevisions()
1828

    
1829
    /**
1830
     * Check if the docId is a data packadge id. If the id is a data packadage
1831
     * id, it should be store in the docId fields in xml_relation table. So we
1832
     * can use a query to get the entries which the docId equals the given
1833
     * value. If the result is null. The docId is not a packadge id. Otherwise,
1834
     * it is.
1835
     *
1836
     * @param docId, the id need to be checked
1837
     */
1838
    private boolean isDataPackageId(String docId)
1839
    {
1840
        boolean result = false;
1841
        PreparedStatement pStmt = null;
1842
        ResultSet rs = null;
1843
        String query = "SELECT docId from xml_relation where docId = ?";
1844
        DBConnection dbConn = null;
1845
        int serialNumber = -1;
1846
        try {
1847
            dbConn = DBConnectionPool
1848
                    .getDBConnection("DBQuery.isDataPackageId");
1849
            serialNumber = dbConn.getCheckOutSerialNumber();
1850
            pStmt = dbConn.prepareStatement(query);
1851
            //bind the value to query
1852
            pStmt.setString(1, docId);
1853
            //execute the query
1854
            pStmt.execute();
1855
            rs = pStmt.getResultSet();
1856
            //process the result
1857
            if (rs.next()) //There are some records for the id in docId fields
1858
            {
1859
                result = true;//It is a data packadge id
1860
            }
1861
            pStmt.close();
1862
        }//try
1863
        catch (SQLException e) {
1864
            logMetacat.error("DBQuery.isDataPackageId - SQL Error: "
1865
                    + e.getMessage());
1866
        } finally {
1867
            try {
1868
                pStmt.close();
1869
            }//try
1870
            catch (SQLException ee) {
1871
                logMetacat.error("DBQuery.isDataPackageId - SQL Error in isDataPackageId: "
1872
                        + ee.getMessage());
1873
            }//catch
1874
            finally {
1875
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1876
            }//finally
1877
        }//finally
1878
        return result;
1879
    }//isDataPackageId()
1880

    
1881
    /**
1882
     * Check if the user has the permission to export data package
1883
     *
1884
     * @param conn, the connection
1885
     * @param docId, the id need to be checked
1886
     * @param user, the name of user
1887
     * @param groups, the user's group
1888
     */
1889
    private boolean hasPermissionToExportPackage(String docId, String user,
1890
            String[] groups) throws Exception
1891
    {
1892
        //DocumentImpl doc=new DocumentImpl(conn,docId);
1893
        return DocumentImpl.hasReadPermission(user, groups, docId);
1894
    }
1895

    
1896
    /**
1897
     * Get the current Rev for a docid in xml_documents table
1898
     *
1899
     * @param docId, the id need to get version numb If the return value is -5,
1900
     *            means no value in rev field for this docid
1901
     */
1902
    private int getCurrentRevFromXMLDoumentsTable(String docId)
1903
            throws SQLException
1904
    {
1905
        int rev = -5;
1906
        PreparedStatement pStmt = null;
1907
        ResultSet rs = null;
1908
        String query = "SELECT rev from xml_documents where docId = ?";
1909
        DBConnection dbConn = null;
1910
        int serialNumber = -1;
1911
        try {
1912
            dbConn = DBConnectionPool
1913
                    .getDBConnection("DBQuery.getCurrentRevFromXMLDocumentsTable");
1914
            serialNumber = dbConn.getCheckOutSerialNumber();
1915
            pStmt = dbConn.prepareStatement(query);
1916
            //bind the value to query
1917
            pStmt.setString(1, docId);
1918
            //execute the query
1919
            pStmt.execute();
1920
            rs = pStmt.getResultSet();
1921
            //process the result
1922
            if (rs.next()) //There are some records for rev
1923
            {
1924
                rev = rs.getInt(1);
1925
                ;//It is the version for given docid
1926
            } else {
1927
                rev = -5;
1928
            }
1929

    
1930
        }//try
1931
        catch (SQLException e) {
1932
            logMetacat.error("DBQuery.getCurrentRevFromXMLDoumentsTable - SQL Error: "
1933
                            + e.getMessage());
1934
            throw e;
1935
        }//catch
1936
        finally {
1937
            try {
1938
                pStmt.close();
1939
            }//try
1940
            catch (SQLException ee) {
1941
                logMetacat.error(
1942
                        "DBQuery.getCurrentRevFromXMLDoumentsTable - SQL Error: "
1943
                                + ee.getMessage());
1944
            }//catch
1945
            finally {
1946
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1947
            }//finally
1948
        }//finally
1949
        return rev;
1950
    }//getCurrentRevFromXMLDoumentsTable
1951

    
1952
    /**
1953
     * put a doc into a zip output stream
1954
     *
1955
     * @param docImpl, docmentImpl object which will be sent to zip output
1956
     *            stream
1957
     * @param zipOut, zip output stream which the docImpl will be put
1958
     * @param packageZipEntry, the zip entry name for whole package
1959
     */
1960
    private void addDocToZipOutputStream(DocumentImpl docImpl,
1961
            ZipOutputStream zipOut, String packageZipEntry)
1962
            throws ClassNotFoundException, IOException, SQLException,
1963
            McdbException, Exception
1964
    {
1965
        byte[] byteString = null;
1966
        ZipEntry zEntry = null;
1967

    
1968
        byteString = docImpl.toString().getBytes();
1969
        //use docId as the zip entry's name
1970
        zEntry = new ZipEntry(packageZipEntry + "/metadata/"
1971
                + docImpl.getDocID());
1972
        zEntry.setSize(byteString.length);
1973
        zipOut.putNextEntry(zEntry);
1974
        zipOut.write(byteString, 0, byteString.length);
1975
        zipOut.closeEntry();
1976

    
1977
    }//addDocToZipOutputStream()
1978

    
1979
    /**
1980
     * Transfer a docid vetor to a documentImpl vector. The documentImpl vetor
1981
     * only inlcudes current version. If a DocumentImple object couldn't find
1982
     * for a docid, then the String of this docid was added to vetor rather
1983
     * than DocumentImple object.
1984
     *
1985
     * @param docIdList, a vetor hold a docid list for a data package. In
1986
     *            docid, there is not version number in it.
1987
     */
1988

    
1989
    private Vector getCurrentAllDocumentImpl(Vector docIdList)
1990
            throws McdbException, Exception
1991
    {
1992
        //Connection dbConn=null;
1993
        Vector documentImplList = new Vector();
1994
        int rev = 0;
1995

    
1996
        // Check the parameter
1997
        if (docIdList.isEmpty()) { return documentImplList; }//if
1998

    
1999
        //for every docid in vector
2000
        for (int i = 0; i < docIdList.size(); i++) {
2001
            try {
2002
                //get newest version for this docId
2003
                rev = getCurrentRevFromXMLDoumentsTable((String) docIdList
2004
                        .elementAt(i));
2005

    
2006
                // There is no record for this docId in xml_documents table
2007
                if (rev == -5) {
2008
                    // Rather than put DocumentImple object, put a String
2009
                    // Object(docid)
2010
                    // into the documentImplList
2011
                    documentImplList.add((String) docIdList.elementAt(i));
2012
                    // Skip other code
2013
                    continue;
2014
                }
2015

    
2016
                String docidPlusVersion = ((String) docIdList.elementAt(i))
2017
                        + PropertyService.getProperty("document.accNumSeparator") + rev;
2018

    
2019
                //create new documentImpl object
2020
                DocumentImpl documentImplObject = new DocumentImpl(
2021
                        docidPlusVersion);
2022
                //add them to vector
2023
                documentImplList.add(documentImplObject);
2024
            }//try
2025
            catch (Exception e) {
2026
                logMetacat.error("DBQuery.getCurrentAllDocumentImpl - General error: "
2027
                        + e.getMessage());
2028
                // continue the for loop
2029
                continue;
2030
            }
2031
        }//for
2032
        return documentImplList;
2033
    }
2034

    
2035
    /**
2036
     * Transfer a docid vetor to a documentImpl vector. If a DocumentImple
2037
     * object couldn't find for a docid, then the String of this docid was
2038
     * added to vetor rather than DocumentImple object.
2039
     *
2040
     * @param docIdList, a vetor hold a docid list for a data package. In
2041
     *            docid, t here is version number in it.
2042
     */
2043
    private Vector getOldVersionAllDocumentImpl(Vector docIdList)
2044
    {
2045
        //Connection dbConn=null;
2046
        Vector documentImplList = new Vector();
2047
        String siteCode = null;
2048
        String uniqueId = null;
2049
        int rev = 0;
2050

    
2051
        // Check the parameter
2052
        if (docIdList.isEmpty()) { return documentImplList; }//if
2053

    
2054
        //for every docid in vector
2055
        for (int i = 0; i < docIdList.size(); i++) {
2056

    
2057
            String docidPlusVersion = (String) (docIdList.elementAt(i));
2058

    
2059
            try {
2060
                //create new documentImpl object
2061
                DocumentImpl documentImplObject = new DocumentImpl(
2062
                        docidPlusVersion);
2063
                //add them to vector
2064
                documentImplList.add(documentImplObject);
2065
            }//try
2066
            catch (McdbDocNotFoundException notFoundE) {
2067
                logMetacat.error("DBQuery.getOldVersionAllDocument - Error finding doc " 
2068
                		+ docidPlusVersion + " : " + notFoundE.getMessage());
2069
                // Rather than add a DocumentImple object into vetor, a String
2070
                // object
2071
                // - the doicd was added to the vector
2072
                documentImplList.add(docidPlusVersion);
2073
                // Continue the for loop
2074
                continue;
2075
            }//catch
2076
            catch (Exception e) {
2077
                logMetacat.error(
2078
                        "DBQuery.getOldVersionAllDocument - General error: "
2079
                                + e.getMessage());
2080
                // Continue the for loop
2081
                continue;
2082
            }//catch
2083

    
2084
        }//for
2085
        return documentImplList;
2086
    }//getOldVersionAllDocumentImple
2087

    
2088
    /**
2089
     * put a data file into a zip output stream
2090
     *
2091
     * @param docImpl, docmentImpl object which will be sent to zip output
2092
     *            stream
2093
     * @param zipOut, the zip output stream which the docImpl will be put
2094
     * @param packageZipEntry, the zip entry name for whole package
2095
     */
2096
    private void addDataFileToZipOutputStream(DocumentImpl docImpl,
2097
            ZipOutputStream zipOut, String packageZipEntry)
2098
            throws ClassNotFoundException, IOException, SQLException,
2099
            McdbException, Exception
2100
    {
2101
        byte[] byteString = null;
2102
        ZipEntry zEntry = null;
2103
        // this is data file; add file to zip
2104
        String filePath = PropertyService.getProperty("application.datafilepath");
2105
        if (!filePath.endsWith("/")) {
2106
            filePath += "/";
2107
        }
2108
        String fileName = filePath + docImpl.getDocID();
2109
        zEntry = new ZipEntry(packageZipEntry + "/data/" + docImpl.getDocID());
2110
        zipOut.putNextEntry(zEntry);
2111
        FileInputStream fin = null;
2112
        try {
2113
            fin = new FileInputStream(fileName);
2114
            byte[] buf = new byte[4 * 1024]; // 4K buffer
2115
            int b = fin.read(buf);
2116
            while (b != -1) {
2117
                zipOut.write(buf, 0, b);
2118
                b = fin.read(buf);
2119
            }//while
2120
            zipOut.closeEntry();
2121
        }//try
2122
        catch (IOException ioe) {
2123
            logMetacat.error("DBQuery.addDataFileToZipOutputStream - I/O error: "
2124
                    + ioe.getMessage());
2125
        }//catch
2126
    }//addDataFileToZipOutputStream()
2127

    
2128
    /**
2129
     * create a html summary for data package and put it into zip output stream
2130
     *
2131
     * @param docImplList, the documentImpl ojbects in data package
2132
     * @param zipOut, the zip output stream which the html should be put
2133
     * @param packageZipEntry, the zip entry name for whole package
2134
     */
2135
    private void addHtmlSummaryToZipOutputStream(Vector docImplList,
2136
            ZipOutputStream zipOut, String packageZipEntry) throws Exception
2137
    {
2138
        StringBuffer htmlDoc = new StringBuffer();
2139
        ZipEntry zEntry = null;
2140
        byte[] byteString = null;
2141
        InputStream source;
2142
        DBTransform xmlToHtml;
2143

    
2144
        //create a DBTransform ojbect
2145
        xmlToHtml = new DBTransform();
2146
        //head of html
2147
        htmlDoc.append("<html><head></head><body>");
2148
        for (int i = 0; i < docImplList.size(); i++) {
2149
            // If this String object, this means it is missed data file
2150
            if ((((docImplList.elementAt(i)).getClass()).toString())
2151
                    .equals("class java.lang.String")) {
2152

    
2153
                htmlDoc.append("<a href=\"");
2154
                String dataFileid = (String) docImplList.elementAt(i);
2155
                htmlDoc.append("./data/").append(dataFileid).append("\">");
2156
                htmlDoc.append("Data File: ");
2157
                htmlDoc.append(dataFileid).append("</a><br>");
2158
                htmlDoc.append("<br><hr><br>");
2159

    
2160
            }//if
2161
            else if ((((DocumentImpl) docImplList.elementAt(i)).getDoctype())
2162
                    .compareTo("BIN") != 0) { //this is an xml file so we can
2163
                                              // transform it.
2164
                //transform each file individually then concatenate all of the
2165
                //transformations together.
2166

    
2167
                //for metadata xml title
2168
                htmlDoc.append("<h2>");
2169
                htmlDoc.append(((DocumentImpl) docImplList.elementAt(i))
2170
                        .getDocID());
2171
                //htmlDoc.append(".");
2172
                //htmlDoc.append(((DocumentImpl)docImplList.elementAt(i)).getRev());
2173
                htmlDoc.append("</h2>");
2174
                //do the actual transform
2175
                StringWriter docString = new StringWriter();
2176
                xmlToHtml.transformXMLDocument(((DocumentImpl) docImplList
2177
                        .elementAt(i)).toString(), "-//NCEAS//eml-generic//EN",
2178
                        "-//W3C//HTML//EN", "html", docString, null, null);
2179
                htmlDoc.append(docString.toString());
2180
                htmlDoc.append("<br><br><hr><br><br>");
2181
            }//if
2182
            else { //this is a data file so we should link to it in the html
2183
                htmlDoc.append("<a href=\"");
2184
                String dataFileid = ((DocumentImpl) docImplList.elementAt(i))
2185
                        .getDocID();
2186
                htmlDoc.append("./data/").append(dataFileid).append("\">");
2187
                htmlDoc.append("Data File: ");
2188
                htmlDoc.append(dataFileid).append("</a><br>");
2189
                htmlDoc.append("<br><hr><br>");
2190
            }//else
2191
        }//for
2192
        htmlDoc.append("</body></html>");
2193
        byteString = htmlDoc.toString().getBytes();
2194
        zEntry = new ZipEntry(packageZipEntry + "/metadata.html");
2195
        zEntry.setSize(byteString.length);
2196
        zipOut.putNextEntry(zEntry);
2197
        zipOut.write(byteString, 0, byteString.length);
2198
        zipOut.closeEntry();
2199
        //dbConn.close();
2200

    
2201
    }//addHtmlSummaryToZipOutputStream
2202

    
2203
    /**
2204
     * put a data packadge into a zip output stream
2205
     *
2206
     * @param docId, which the user want to put into zip output stream,it has version
2207
     * @param out, a servletoutput stream which the zip output stream will be
2208
     *            put
2209
     * @param user, the username of the user
2210
     * @param groups, the group of the user
2211
     */
2212
    public ZipOutputStream getZippedPackage(String docIdString,
2213
            ServletOutputStream out, String user, String[] groups,
2214
            String passWord) throws ClassNotFoundException, IOException,
2215
            SQLException, McdbException, NumberFormatException, Exception
2216
    {
2217
        ZipOutputStream zOut = null;
2218
        String elementDocid = null;
2219
        DocumentImpl docImpls = null;
2220
        //Connection dbConn = null;
2221
        Vector docIdList = new Vector();
2222
        Vector documentImplList = new Vector();
2223
        Vector htmlDocumentImplList = new Vector();
2224
        String packageId = null;
2225
        String rootName = "package";//the package zip entry name
2226

    
2227
        String docId = null;
2228
        int version = -5;
2229
        // Docid without revision
2230
        docId = DocumentUtil.getDocIdFromString(docIdString);
2231
        // revision number
2232
        version = DocumentUtil.getVersionFromString(docIdString);
2233

    
2234
        //check if the reqused docId is a data package id
2235
        if (!isDataPackageId(docId)) {
2236

    
2237
            /*
2238
             * Exception e = new Exception("The request the doc id "
2239
             * +docIdString+ " is not a data package id");
2240
             */
2241

    
2242
            //CB 1/6/03: if the requested docid is not a datapackage, we just
2243
            // zip
2244
            //up the single document and return the zip file.
2245
            if (!hasPermissionToExportPackage(docId, user, groups)) {
2246

    
2247
                Exception e = new Exception("User " + user
2248
                        + " does not have permission"
2249
                        + " to export the data package " + docIdString);
2250
                throw e;
2251
            }
2252

    
2253
            docImpls = new DocumentImpl(docIdString);
2254
            //checking if the user has the permission to read the documents
2255
            if (DocumentImpl.hasReadPermission(user, groups, docImpls
2256
                    .getDocID())) {
2257
                zOut = new ZipOutputStream(out);
2258
                //if the docImpls is metadata
2259
                if ((docImpls.getDoctype()).compareTo("BIN") != 0) {
2260
                    //add metadata into zip output stream
2261
                    addDocToZipOutputStream(docImpls, zOut, rootName);
2262
                }//if
2263
                else {
2264
                    //it is data file
2265
                    addDataFileToZipOutputStream(docImpls, zOut, rootName);
2266
                    htmlDocumentImplList.add(docImpls);
2267
                }//else
2268
            }//if
2269

    
2270
            zOut.finish(); //terminate the zip file
2271
            return zOut;
2272
        }
2273
        // Check the permission of user
2274
        else if (!hasPermissionToExportPackage(docId, user, groups)) {
2275

    
2276
            Exception e = new Exception("User " + user
2277
                    + " does not have permission"
2278
                    + " to export the data package " + docIdString);
2279
            throw e;
2280
        } else //it is a packadge id
2281
        {
2282
            //store the package id
2283
            packageId = docId;
2284
            //get current version in database
2285
            int currentVersion = getCurrentRevFromXMLDoumentsTable(packageId);
2286
            //If it is for current version (-1 means user didn't specify
2287
            // revision)
2288
            if ((version == -1) || version == currentVersion) {
2289
                //get current version number
2290
                version = currentVersion;
2291
                //get package zip entry name
2292
                //it should be docId.revsion.package
2293
                rootName = packageId + PropertyService.getProperty("document.accNumSeparator")
2294
                        + version + PropertyService.getProperty("document.accNumSeparator")
2295
                        + "package";
2296
                //get the whole id list for data packadge
2297
                docIdList = getCurrentDocidListForDataPackage(packageId);
2298
                //get the whole documentImple object
2299
                documentImplList = getCurrentAllDocumentImpl(docIdList);
2300

    
2301
            }//if
2302
            else if (version > currentVersion || version < -1) {
2303
                throw new Exception("The user specified docid: " + docId + "."
2304
                        + version + " doesn't exist");
2305
            }//else if
2306
            else //for an old version
2307
            {
2308

    
2309
                rootName = docIdString
2310
                        + PropertyService.getProperty("document.accNumSeparator") + "package";
2311
                //get the whole id list for data packadge
2312
                docIdList = getOldVersionDocidListForDataPackage(docIdString);
2313

    
2314
                //get the whole documentImple object
2315
                documentImplList = getOldVersionAllDocumentImpl(docIdList);
2316
            }//else
2317

    
2318
            // Make sure documentImplist is not empty
2319
            if (documentImplList.isEmpty()) { throw new Exception(
2320
                    "Couldn't find component for data package: " + packageId); }//if
2321

    
2322
            zOut = new ZipOutputStream(out);
2323
            //put every element into zip output stream
2324
            for (int i = 0; i < documentImplList.size(); i++) {
2325
                // if the object in the vetor is String, this means we couldn't
2326
                // find
2327
                // the document locally, we need find it remote
2328
                if ((((documentImplList.elementAt(i)).getClass()).toString())
2329
                        .equals("class java.lang.String")) {
2330
                    // Get String object from vetor
2331
                    String documentId = (String) documentImplList.elementAt(i);
2332
                    logMetacat.info("DBQuery.getZippedPackage - docid: " + documentId);
2333
                    // Get doicd without revision
2334
                    String docidWithoutRevision = 
2335
                    	DocumentUtil.getDocIdFromString(documentId);
2336
                    logMetacat.info("DBQuery.getZippedPackage - docidWithoutRevsion: "
2337
                            + docidWithoutRevision);
2338
                    // Get revision
2339
                    String revision = 
2340
                    	DocumentUtil.getRevisionStringFromString(documentId);
2341
                    logMetacat.info("DBQuery.getZippedPackage - revision from docIdentifier: "
2342
                            + revision);
2343
                    // Zip entry string
2344
                    String zipEntryPath = rootName + "/data/";
2345
                    // Create a RemoteDocument object
2346
                    RemoteDocument remoteDoc = new RemoteDocument(
2347
                            docidWithoutRevision, revision, user, passWord,
2348
                            zipEntryPath);
2349
                    // Here we only read data file from remote metacat
2350
                    String docType = remoteDoc.getDocType();
2351
                    if (docType != null) {
2352
                        if (docType.equals("BIN")) {
2353
                            // Put remote document to zip output
2354
                            remoteDoc.readDocumentFromRemoteServerByZip(zOut);
2355
                            // Add String object to htmlDocumentImplList
2356
                            String elementInHtmlList = remoteDoc
2357
                                    .getDocIdWithoutRevsion()
2358
                                    + PropertyService.getProperty("document.accNumSeparator")
2359
                                    + remoteDoc.getRevision();
2360
                            htmlDocumentImplList.add(elementInHtmlList);
2361
                        }//if
2362
                    }//if
2363

    
2364
                }//if
2365
                else {
2366
                    //create a docmentImpls object (represent xml doc) base on
2367
                    // the docId
2368
                    docImpls = (DocumentImpl) documentImplList.elementAt(i);
2369
                    //checking if the user has the permission to read the
2370
                    // documents
2371
                    if (DocumentImpl.hasReadPermission(user, groups, docImpls
2372
                            .getDocID())) {
2373
                        //if the docImpls is metadata
2374
                        if ((docImpls.getDoctype()).compareTo("BIN") != 0) {
2375
                            //add metadata into zip output stream
2376
                            addDocToZipOutputStream(docImpls, zOut, rootName);
2377
                            //add the documentImpl into the vetor which will
2378
                            // be used in html
2379
                            htmlDocumentImplList.add(docImpls);
2380

    
2381
                        }//if
2382
                        else {
2383
                            //it is data file
2384
                            addDataFileToZipOutputStream(docImpls, zOut,
2385
                                    rootName);
2386
                            htmlDocumentImplList.add(docImpls);
2387
                        }//else
2388
                    }//if
2389
                }//else
2390
            }//for
2391

    
2392
            //add html summary file
2393
            addHtmlSummaryToZipOutputStream(htmlDocumentImplList, zOut,
2394
                    rootName);
2395
            zOut.finish(); //terminate the zip file
2396
            //dbConn.close();
2397
            return zOut;
2398
        }//else
2399
    }//getZippedPackage()
2400

    
2401
    private class ReturnFieldValue
2402
    {
2403

    
2404
        private String docid = null; //return field value for this docid
2405

    
2406
        private String fieldValue = null;
2407

    
2408
        private String xmlFieldValue = null; //return field value in xml
2409
                                             // format
2410
        private String fieldType = null; //ATTRIBUTE, TEXT...
2411

    
2412
        public void setDocid(String myDocid)
2413
        {
2414
            docid = myDocid;
2415
        }
2416

    
2417
        public String getDocid()
2418
        {
2419
            return docid;
2420
        }
2421

    
2422
        public void setFieldValue(String myValue)
2423
        {
2424
            fieldValue = myValue;
2425
        }
2426

    
2427
        public String getFieldValue()
2428
        {
2429
            return fieldValue;
2430
        }
2431

    
2432
        public void setXMLFieldValue(String xml)
2433
        {
2434
            xmlFieldValue = xml;
2435
        }
2436

    
2437
        public String getXMLFieldValue()
2438
        {
2439
            return xmlFieldValue;
2440
        }
2441
        
2442
        public void setFieldType(String myType)
2443
        {
2444
            fieldType = myType;
2445
        }
2446

    
2447
        public String getFieldType()
2448
        {
2449
            return fieldType;
2450
        }
2451

    
2452
    }
2453
    
2454
    /**
2455
     * a class to store one result document consisting of a docid and a document
2456
     */
2457
    private class ResultDocument
2458
    {
2459
      public String docid;
2460
      public String document;
2461
      
2462
      public ResultDocument(String docid, String document)
2463
      {
2464
        this.docid = docid;
2465
        this.document = document;
2466
      }
2467
    }
2468
    
2469
    /**
2470
     * a private class to handle a set of resultDocuments
2471
     */
2472
    private class ResultDocumentSet
2473
    {
2474
      private Vector docids;
2475
      private Vector documents;
2476
      
2477
      public ResultDocumentSet()
2478
      {
2479
        docids = new Vector();
2480
        documents = new Vector();
2481
      }
2482
      
2483
      /**
2484
       * adds a result document to the set
2485
       */
2486
      public void addResultDocument(ResultDocument rd)
2487
      {
2488
        if(rd.docid == null)
2489
          return;
2490
        if(rd.document == null)
2491
          rd.document = "";
2492
       
2493
           docids.addElement(rd.docid);
2494
           documents.addElement(rd.document);
2495
        
2496
      }
2497
      
2498
      /**
2499
       * gets an iterator of docids
2500
       */
2501
      public Iterator getDocids()
2502
      {
2503
        return docids.iterator();
2504
      }
2505
      
2506
      /**
2507
       * gets an iterator of documents
2508
       */
2509
      public Iterator getDocuments()
2510
      {
2511
        return documents.iterator();
2512
      }
2513
      
2514
      /**
2515
       * returns the size of the set
2516
       */
2517
      public int size()
2518
      {
2519
        return docids.size();
2520
      }
2521
      
2522
      /**
2523
       * tests to see if this set contains the given docid
2524
       */
2525
      private boolean containsDocid(String docid)
2526
      {
2527
        for(int i=0; i<docids.size(); i++)
2528
        {
2529
          String docid0 = (String)docids.elementAt(i);
2530
          if(docid0.trim().equals(docid.trim()))
2531
          {
2532
            return true;
2533
          }
2534
        }
2535
        return false;
2536
      }
2537
      
2538
      /**
2539
       * removes the element with the given docid
2540
       */
2541
      public String remove(String docid)
2542
      {
2543
        for(int i=0; i<docids.size(); i++)
2544
        {
2545
          String docid0 = (String)docids.elementAt(i);
2546
          if(docid0.trim().equals(docid.trim()))
2547
          {
2548
            String returnDoc = (String)documents.elementAt(i);
2549
            documents.remove(i);
2550
            docids.remove(i);
2551
            return returnDoc;
2552
          }
2553
        }
2554
        return null;
2555
      }
2556
      
2557
      /**
2558
       * add a result document
2559
       */
2560
      public void put(ResultDocument rd)
2561
      {
2562
        addResultDocument(rd);
2563
      }
2564
      
2565
      /**
2566
       * add a result document by components
2567
       */
2568
      public void put(String docid, String document)
2569
      {
2570
        addResultDocument(new ResultDocument(docid, document));
2571
      }
2572
      
2573
      /**
2574
       * get the document part of the result document by docid
2575
       */
2576
      public Object get(String docid)
2577
      {
2578
        for(int i=0; i<docids.size(); i++)
2579
        {
2580
          String docid0 = (String)docids.elementAt(i);
2581
          if(docid0.trim().equals(docid.trim()))
2582
          {
2583
            return documents.elementAt(i);
2584
          }
2585
        }
2586
        return null;
2587
      }
2588
      
2589
      /**
2590
       * get the document part of the result document by an object
2591
       */
2592
      public Object get(Object o)
2593
      {
2594
        return get((String)o);
2595
      }
2596
      
2597
      /**
2598
       * get an entire result document by index number
2599
       */
2600
      public ResultDocument get(int index)
2601
      {
2602
        return new ResultDocument((String)docids.elementAt(index), 
2603
          (String)documents.elementAt(index));
2604
      }
2605
      
2606
      /**
2607
       * return a string representation of this object
2608
       */
2609
      public String toString()
2610
      {
2611
        String s = "";
2612
        for(int i=0; i<docids.size(); i++)
2613
        {
2614
          s += (String)docids.elementAt(i) + "\n";
2615
        }
2616
        return s;
2617
      }
2618
      /*
2619
       * Set a new document value for a given docid
2620
       */
2621
      public void set(String docid, String document)
2622
      {
2623
    	   for(int i=0; i<docids.size(); i++)
2624
           {
2625
             String docid0 = (String)docids.elementAt(i);
2626
             if(docid0.trim().equals(docid.trim()))
2627
             {
2628
                 documents.set(i, document);
2629
             }
2630
           }
2631
           
2632
      }
2633
    }
2634
}
(17-17/60)