Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents a structured query, and can be
4
 *             constructed from an XML serialization conforming to
5
 *             pathquery.dtd. The printSQL() method can be used to print
6
 *             a SQL serialization of the query.
7
 *  Copyright: 2000 Regents of the University of California and the
8
 *             National Center for Ecological Analysis and Synthesis
9
 *    Authors: Matt Jones
10
 *
11
 *   '$Author: daigle $'
12
 *     '$Date: 2008-08-29 10:20:19 -0700 (Fri, 29 Aug 2008) $'
13
 * '$Revision: 4335 $'
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
 */
29

    
30
package edu.ucsb.nceas.metacat;
31

    
32
import java.io.IOException;
33
import java.io.Reader;
34
import java.io.StringReader;
35
import java.util.Enumeration;
36
import java.util.Stack;
37
import java.util.Vector;
38

    
39
import edu.ucsb.nceas.metacat.util.MetaCatUtil;
40

    
41
import org.apache.log4j.Logger;
42
import org.xml.sax.Attributes;
43
import org.xml.sax.InputSource;
44
import org.xml.sax.SAXException;
45
import org.xml.sax.XMLReader;
46
import org.xml.sax.helpers.DefaultHandler;
47
import org.xml.sax.helpers.XMLReaderFactory;
48
import java.util.Iterator;
49

    
50
/**
51
 * A Class that represents a structured query, and can be constructed from an
52
 * XML serialization conforming to
53
 *
54
 * @see pathquery.dtd. The printSQL() method can be used to print a SQL
55
 *      serialization of the query.
56
 */
57
public class QuerySpecification extends DefaultHandler
58
{
59

    
60
    /** flag determining whether extended query terms are present */
61
    private boolean containsExtendedSQL = false;
62

    
63
    /** flag determining whether predicates are present */
64
    private boolean containsPredicates = false;
65

    
66
    /** Identifier for this query document */
67
    private String meta_file_id;
68

    
69
    /** Title of this query */
70
    private String queryTitle;
71

    
72
    /** List of document types to be returned using package back tracing */
73
    private Vector returnDocList;
74

    
75
    /** List of document types to be searched */
76
    private Vector filterDocList;
77

    
78
    /** List of fields to be returned in result set */
79
    private Vector returnFieldList;
80
    
81
    /** List of fields with "[" and "]" in result set. This is a subset of returnFieldList.
82
     *   If some of return fields have [,  those fields will be stored this vector (we have different query for those return fields */
83
    private Vector returnFieldListWithPredicates;
84

    
85
    /** List of users owning documents to be searched */
86
    private Vector ownerList;
87

    
88
    /** The root query group that contains the recursive query constraints */
89
    private QueryGroup query = null;
90
    
91
    /** A string buffer to stored normalized query (Sometimes, the query have 
92
     * a value like "&", it will cause problem in html transform). So we need a
93
     * normalized query xml string.
94
     */
95
    private StringBuffer xml = new StringBuffer();
96

    
97
    // Query data structures used temporarily during XML parsing
98
    private Stack elementStack;
99

    
100
    private Stack queryStack;
101

    
102
    private String currentValue;
103

    
104
    private String currentPathexpr;
105

    
106
    private String parserName = null;
107

    
108
    private String accNumberSeparator = null;
109

    
110
    private boolean percentageSearch = false;
111

    
112
    private String userName = null;
113

    
114
    private static final String PUBLIC = "public";
115

    
116
    private String[] group = null;
117

    
118
    public static final String ATTRIBUTESYMBOL = "@";
119

    
120
    public static final char PREDICATE_START = '[';
121

    
122
    public static final char PREDICATE_END = ']';
123

    
124
    //private boolean hasAttributeReturnField = false;
125

    
126
    //private Hashtable attributeReturnList = new Hashtable();
127

    
128
    //private int countAttributeReturnField = 0;
129

    
130
    private StringBuffer textBuffer = new StringBuffer();
131
    
132
   
133
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
134

    
135
    /**
136
     * construct an instance of the QuerySpecification class
137
     *
138
     * @param queryspec
139
     *            the XML representation of the query (should conform to
140
     *            pathquery.dtd) as a Reader
141
     * @param parserName
142
     *            the fully qualified name of a Java Class implementing the
143
     *            org.xml.sax.XMLReader interface
144
     */
145
    public QuerySpecification(Reader queryspec, String parserName,
146
            String accNumberSeparator) throws IOException
147
    {
148
        super();
149

    
150
        // Initialize the class variables
151
        returnDocList = new Vector();
152
        filterDocList = new Vector();
153
        elementStack = new Stack();
154
        queryStack = new Stack();
155
        returnFieldList = new Vector();
156
        returnFieldListWithPredicates = new Vector();
157
        ownerList = new Vector();
158
        this.parserName = parserName;
159
        this.accNumberSeparator = accNumberSeparator;
160

    
161
        // Initialize the parser and read the queryspec
162
        XMLReader parser = initializeParser();
163
        if (parser == null) {
164
            System.err.println("SAX parser not instantiated properly.");
165
        }
166
        try {
167
            parser.parse(new InputSource(queryspec));
168
        } catch (SAXException e) {
169
            System.err.println("error parsing data in "
170
                    + "QuerySpecification.QuerySpecification");
171
            System.err.println(e.getMessage());
172
        }
173
    }
174

    
175
    /**
176
     * construct an instance of the QuerySpecification class
177
     *
178
     * @param queryspec
179
     *            the XML representation of the query (should conform to
180
     *            pathquery.dtd) as a String
181
     * @param parserName
182
     *            the fully qualified name of a Java Class implementing the
183
     *            org.xml.sax.Parser interface
184
     */
185
    public QuerySpecification(String queryspec, String parserName,
186
            String accNumberSeparator) throws IOException
187
    {
188
        this(new StringReader(queryspec), parserName, accNumberSeparator);
189
    }
190

    
191
    /**
192
     * construct an instance of the QuerySpecification class which don't need
193
     * to parser a xml document
194
     *
195
     * @param accNumberSeparator
196
     *            the separator between doc version
197
     */
198
    public QuerySpecification(String accNumberSeparator) throws IOException
199
    {
200
        // Initialize the class variables
201
        returnDocList = new Vector();
202
        filterDocList = new Vector();
203
        elementStack = new Stack();
204
        queryStack = new Stack();
205
        returnFieldList = new Vector();
206
        returnFieldListWithPredicates = new Vector();
207
        ownerList = new Vector();
208
        this.accNumberSeparator = accNumberSeparator;
209
    }
210

    
211
    /**
212
     * Method to set user name
213
     *
214
     * @param myName
215
     *            the user name
216
     */
217
    public void setUserName(String myName)
218
    {
219
        //to lower case
220
        if (myName != null) {
221
            this.userName = myName.toLowerCase();
222
        } else {
223
            this.userName = myName;
224
        }
225
    }
226

    
227
    /**
228
     * Method to set user group
229
     *
230
     * @param myGroup
231
     *            the user group
232
     */
233
    public void setGroup(String[] myGroup)
234
    {
235
        this.group = myGroup;
236
    }
237

    
238
    /**
239
     * Method to indicate this query is a percentage search
240
     */
241
    public boolean isPercentageSearch()
242
    {
243
        return percentageSearch;
244
    }
245

    
246
    /*
247
     * Method to get owner query. If it is owner it has all permission
248
     */
249
    private String createOwerQuery()
250
    {
251
        String ownerQuery = null;
252
        //if user is public, we don't need to run owner query
253
        if (userName != null && !userName.equalsIgnoreCase(PUBLIC))
254
        {
255
	        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
256
	        if (userName != null && !userName.equals("")) {
257
	            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
258
	        }
259
        }
260
        logMetacat.info("OwnerQuery: " + ownerQuery);
261
        return ownerQuery;
262
    }
263

    
264
    /*
265
     * Method to create query for xml_access, this part is to get docid list
266
     * which have a allow rule for a given user
267
     */
268
    private String createAllowRuleQuery()
269
    {
270
        String allowQuery = null;
271
        String allowString = constructAllowString();
272
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
273
        allowQuery = allowQuery + ")";
274
        logMetacat.info("allow query is: " + allowQuery);
275
        return allowQuery;
276

    
277
    }
278

    
279
    /* Method to construct a allow rule string */
280
    private String constructAllowString()
281
    {
282
        String allowQuery = "";
283
        
284
       // add public
285
        allowQuery = "(lower(principal_name) = '" + PUBLIC
286
                + "'";
287
                
288
        // add user name
289
        if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
290
            allowQuery = allowQuery + "OR lower(principal_name) = '" + userName +"'";
291
                    
292
        }
293
        // add  group
294
        if (group != null) {
295
            for (int i = 0; i < group.length; i++) {
296
                String groupUint = group[i];
297
                if (groupUint != null && !groupUint.equals("")) {
298
                    groupUint = groupUint.toLowerCase();
299
                    allowQuery = allowQuery + " OR lower(principal_name) = '"
300
                            + groupUint + "'";
301
                }//if
302
            }//for
303
        }//if
304
        // add allow rule
305
        allowQuery = allowQuery + ") AND perm_type = 'allow'" + " AND permission > 3";
306
        logMetacat.info("allow string is: " + allowQuery);
307
        return allowQuery;
308
    }
309

    
310
    /*
311
     * Method to create query for xml_access, this part is to get docid list
312
     * which have a deny rule and perm_order is allowFirst for a given user.
313
     * This means the user will be denied to read
314
     */
315
    private String createDenyRuleQuery()
316
    {
317
        String denyQuery = null;
318
        String denyString = constructDenyString();
319
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
320
        denyQuery = denyQuery + ") ";
321
        logMetacat.info("denyquery is: " + denyQuery);
322
        return denyQuery;
323

    
324
    }
325

    
326
    /* Construct deny string */
327
    private String constructDenyString()
328
    {
329
        String denyQuery = "";
330
         
331
        // add public
332
        denyQuery = "(lower(principal_name) = '" + PUBLIC
333
                 + "'";
334
                 
335
         // add user name
336
         if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
337
        	 denyQuery = denyQuery + "OR lower(principal_name) = '" + userName +"'";
338
                     
339
         }
340
         // add  groups
341
         if (group != null) {
342
             for (int i = 0; i < group.length; i++) {
343
                 String groupUint = group[i];
344
                 if (groupUint != null && !groupUint.equals("")) {
345
                     groupUint = groupUint.toLowerCase();
346
                     denyQuery = denyQuery + " OR lower(principal_name) = '"
347
                             + groupUint + "'";
348
                 }//if
349
             }//for
350
         }//if
351
         // add deny rules
352
         denyQuery = denyQuery + ") AND perm_type = 'deny'" +  " AND perm_order ='allowFirst'" +" AND permission > 3";
353
         logMetacat.info("allow string is: " + denyQuery);
354
         return denyQuery;
355
        
356
    }
357

    
358
    /**
359
     * Method to append a access control query to SQL. So in DBQuery class, we
360
     * can get docid from both user specified query and access control query.
361
     * We don't need to checking permission after we get the doclist. It will
362
     * be good to performance
363
     *
364
     */
365
    public String getAccessQuery()
366
    {
367
        String accessQuery = null;
368
        String onwer = createOwerQuery();
369
        String allow = createAllowRuleQuery();
370
        String deny = createDenyRuleQuery();
371
        //logMetacat.warn("onwer " +onwer);
372
        //logMetacat.warn("allow "+allow);
373
        //logMetacat.warn("deny "+deny);
374
        if (onwer != null)
375
        {
376
          accessQuery = " AND (docid IN(" + onwer + ")";
377
          accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
378
                + " AND docid NOT IN (" + deny + ")))";
379
        }
380
        else
381
        {
382
        	accessQuery = " AND (docid IN (" + allow + ")"
383
                + " AND docid NOT IN (" + deny + "))";
384
        }
385
        logMetacat.warn("accessquery is: " + accessQuery);
386
        return accessQuery;
387
    }
388

    
389
    /**
390
     * Returns true if the parsed query contains and extended xml query (i.e.
391
     * there is at least one &lt;returnfield&gt; in the pathquery document)
392
     */
393
    public boolean containsExtendedSQL()
394
    {
395
        if (containsExtendedSQL) {
396
            return true;
397
        } else {
398
            return false;
399
        }
400
    }
401

    
402
  
403
    /**
404
     * Accessor method to return the identifier of this Query
405
     */
406
    public String getIdentifier()
407
    {
408
        return meta_file_id;
409
    }
410

    
411
    /**
412
     * method to set the identifier of this query
413
     */
414
    public void setIdentifier(String id)
415
    {
416
        this.meta_file_id = id;
417
    }
418

    
419
    /**
420
     * Accessor method to return the title of this Query
421
     */
422
    public String getQueryTitle()
423
    {
424
        return queryTitle;
425
    }
426

    
427
    /**
428
     * method to set the title of this query
429
     */
430
    public void setQueryTitle(String title)
431
    {
432
        this.queryTitle = title;
433
    }
434

    
435
    /**
436
     * Accessor method to return a vector of the return document types as
437
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
438
     */
439
    public Vector getReturnDocList()
440
    {
441
        return this.returnDocList;
442
    }
443

    
444
    /**
445
     * method to set the list of return docs of this query
446
     */
447
    public void setReturnDocList(Vector returnDocList)
448
    {
449
        this.returnDocList = returnDocList;
450
    }
451

    
452
    /**
453
     * Accessor method to return a vector of the filter doc types as defined in
454
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
455
     */
456
    public Vector getFilterDocList()
457
    {
458
        return this.filterDocList;
459
    }
460

    
461
    /**
462
     * method to set the list of filter docs of this query
463
     */
464
    public void setFilterDocList(Vector filterDocList)
465
    {
466
        this.filterDocList = filterDocList;
467
    }
468

    
469
    /**
470
     * Accessor method to return a vector of the extended return fields as
471
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
472
     */
473
    public Vector getReturnFieldList()
474
    {
475
        return this.returnFieldList;
476
    }
477

    
478
    /**
479
     * method to set the list of fields to be returned by this query
480
     */
481
    public void setReturnFieldList(Vector returnFieldList)
482
    {
483
        this.returnFieldList = returnFieldList;
484
    }
485

    
486
    /**
487
     * Accessor method to return a vector of the owner fields as defined in the
488
     * &lt;owner&gt; tag in the pathquery dtd.
489
     */
490
    public Vector getOwnerList()
491
    {
492
        return this.ownerList;
493
    }
494

    
495
    /**
496
     * method to set the list of owners used to constrain this query
497
     */
498
    public void setOwnerList(Vector ownerList)
499
    {
500
        this.ownerList = ownerList;
501
    }
502

    
503
    /**
504
     * get the QueryGroup used to express query constraints
505
     */
506
    public QueryGroup getQueryGroup()
507
    {
508
        return query;
509
    }
510

    
511
    /**
512
     * set the querygroup
513
     */
514
    public void setQueryGroup(QueryGroup group)
515
    {
516
        query = group;
517
    }
518

    
519
    /**
520
     * set if this query sepcification has extendQuery(has return doc type or
521
     * not)
522
     */
523
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
524
    {
525
        containsExtendedSQL = hasExtenedQuery;
526
    }
527

    
528
    /**
529
     * Set up the SAX parser for reading the XML serialized query
530
     */
531
    private XMLReader initializeParser()
532
    {
533
        XMLReader parser = null;
534

    
535
        // Set up the SAX document handlers for parsing
536
        try {
537

    
538
            // Get an instance of the parser
539
            parser = XMLReaderFactory.createXMLReader(parserName);
540

    
541
            // Set the ContentHandler to this instance
542
            parser.setContentHandler(this);
543

    
544
            // Set the error Handler to this instance
545
            parser.setErrorHandler(this);
546

    
547
        } catch (Exception e) {
548
            System.err.println("Error in QuerySpcecification.initializeParser "
549
                    + e.toString());
550
        }
551

    
552
        return parser;
553
    }
554

    
555
    /**
556
     * callback method used by the SAX Parser when the start tag of an element
557
     * is detected. Used in this context to parse and store the query
558
     * information in class variables.
559
     */
560
    public void startElement(String uri, String localName, String qName,
561
            Attributes atts) throws SAXException
562
    {
563
        logMetacat.debug("start at startElement "+localName);
564
        BasicNode currentNode = new BasicNode(localName);
565
        //write element name into xml buffer.
566
        xml.append("<");
567
        xml.append(localName);
568
        // add attributes to BasicNode here
569
        if (atts != null) {
570
            int len = atts.getLength();
571
            for (int i = 0; i < len; i++) {
572
                currentNode
573
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
574
                xml.append(" ");
575
                xml.append(atts.getLocalName(i));
576
                xml.append("=\"");
577
                xml.append(atts.getValue(i));
578
                xml.append("\"");
579
            }
580
        }
581
        xml.append(">");
582

    
583
        elementStack.push(currentNode);
584
        if (currentNode.getTagName().equals("querygroup")) {
585
            QueryGroup currentGroup = new QueryGroup(currentNode
586
                    .getAttribute("operator"));
587
            if (query == null) {
588
                query = currentGroup;
589
            } else {
590
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
591
                parentGroup.addChild(currentGroup);
592
            }
593
            queryStack.push(currentGroup);
594
        }
595
        logMetacat.debug("end in startElement "+localName);
596
    }
597

    
598
    /**
599
     * callback method used by the SAX Parser when the end tag of an element is
600
     * detected. Used in this context to parse and store the query information
601
     * in class variables.
602
     */
603
    public void endElement(String uri, String localName, String qName)
604
            throws SAXException
605
    {
606
    	 logMetacat.debug("start in endElement "+localName);
607
        BasicNode leaving = (BasicNode) elementStack.pop();
608
        if (leaving.getTagName().equals("queryterm")) {
609
            boolean isCaseSensitive = (new Boolean(leaving
610
                    .getAttribute("casesensitive"))).booleanValue();
611
            QueryTerm currentTerm = null;
612
            if (currentPathexpr == null) {
613
                currentTerm = new QueryTerm(isCaseSensitive, leaving
614
                        .getAttribute("searchmode"), currentValue);
615
            } else {
616
                currentTerm = new QueryTerm(isCaseSensitive, leaving
617
                        .getAttribute("searchmode"), currentValue,
618
                        currentPathexpr);
619
            }
620
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
621
            currentGroup.addChild(currentTerm);
622
            currentValue = null;
623
            currentPathexpr = null;
624
        } else if (leaving.getTagName().equals("querygroup")) {
625
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
626
        } else if (leaving.getTagName().equals("meta_file_id")) {
627
              meta_file_id = textBuffer.toString().trim();
628
        } else if (leaving.getTagName().equals("querytitle")) {
629
              queryTitle = textBuffer.toString().trim();
630
        } else if (leaving.getTagName().equals("value")) {
631
              currentValue = textBuffer.toString().trim();
632
              currentValue = MetaCatUtil.normalize(currentValue);
633
        } else if (leaving.getTagName().equals("pathexpr")) {
634
              currentPathexpr = textBuffer.toString().trim();
635
        } else if (leaving.getTagName().equals("returndoctype")) {
636
              returnDocList.add(textBuffer.toString().trim());
637
        } else if (leaving.getTagName().equals("filterdoctype")) {
638
              filterDocList.add(textBuffer.toString().trim());
639
        } else if (leaving.getTagName().equals("returnfield")) {
640
              handleReturnField(textBuffer.toString().trim());
641
        } else if (leaving.getTagName().equals("filterdoctype")) {
642
              filterDocList.add(textBuffer.toString().trim());
643
        } else if (leaving.getTagName().equals("owner")) {
644
              ownerList.add(textBuffer.toString().trim());
645
        }
646
        String normalizedXML = textBuffer.toString().trim();
647
        logMetacat.debug("================before normailze "+normalizedXML);
648
        normalizedXML =  MetaCatUtil.normalize(normalizedXML);
649
        logMetacat.debug("================after normailze "+normalizedXML);
650
        xml.append(normalizedXML);
651
        xml.append("</");
652
        xml.append(localName);
653
        xml.append(">");
654
        //rest textBuffer
655
        textBuffer = new StringBuffer();
656

    
657
    }
658
    
659
    /**
660
     * Gets normailized query string in xml format, which can be transformed
661
     * to html
662
     */
663
    public String getNormalizedXMLQuery()
664
    {
665
    	//System.out.println("normailized xml \n"+xml.toString());
666
    	return xml.toString();
667
    }
668
    
669

    
670
    /**
671
     * callback method used by the SAX Parser when the text sequences of an xml
672
     * stream are detected. Used in this context to parse and store the query
673
     * information in class variables.
674
     */
675
    public void characters(char ch[], int start, int length)
676
    {
677
      // buffer all text nodes for same element. This is for text was splited
678
      // into different nodes
679
      String text = new String(ch, start, length);
680
      logMetacat.debug("the text in characters "+text);
681
      textBuffer.append(text);
682

    
683
    }
684

    
685
   /**
686
    * Method to handle return field. It will be callied in ecogrid part
687
    * @param inputString
688
    */
689
    public void handleReturnField(String inputString)
690
    {
691
        int attributePos = inputString.indexOf(ATTRIBUTESYMBOL);
692
        int predicateStart = -1;
693
        int predicateEnd;
694
        boolean hasPredicate = false;
695

    
696
        while (true)
697
        {
698
            predicateStart = inputString.indexOf(PREDICATE_START, predicateStart + 1);
699

    
700
            if (attributePos == -1)
701
                break;
702

    
703
            if (predicateStart == -1)
704
                break;
705

    
706
            hasPredicate = true;
707

    
708
            if (attributePos < predicateStart)
709
                break;
710

    
711
            predicateEnd = inputString.indexOf(PREDICATE_END, predicateStart);
712

    
713
            if (predicateEnd == -1)
714
            {
715
                logMetacat.warn("handleReturnField(): ");
716
                logMetacat.warn("    Invalid path: " + inputString);
717
                return;
718
            }
719

    
720
            while (attributePos < predicateEnd)
721
            {
722
                attributePos = inputString.indexOf(ATTRIBUTESYMBOL, attributePos + 1);
723

    
724
                if (attributePos == -1)
725
                    break;
726
            }
727
        }
728

    
729
        if (hasPredicate)
730
        {
731
            containsPredicates = true;
732
            returnFieldListWithPredicates.add(inputString);
733
        }
734

    
735
        containsExtendedSQL = true;
736

    
737
     
738
        // no attribute value will be returned
739
        logMetacat.info("QuerySpecification.handleReturnField(): " );
740
        logMetacat.info("  there are no attributes in the XPATH statement" );
741
        returnFieldList.add(inputString);
742
       
743

    
744
       
745
    }
746

    
747
    /**
748
     * create a SQL serialization of the query that this instance represents
749
     */
750
    public String printSQL(boolean useXMLIndex)
751
    {
752

    
753
        StringBuffer self = new StringBuffer();
754
        StringBuffer queryString = new StringBuffer();
755

    
756
        queryString.append("SELECT docid,docname,doctype,");
757
        queryString.append("date_created, date_updated, rev ");
758
        queryString.append("FROM xml_documents WHERE");
759

    
760
        // Get the query from the QueryGroup and check
761
        // if no query has been returned
762
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
763
        logMetacat.info("Query from query in QuerySpec.printSQL: " 
764
        		+ queryFromQueryGroup);
765
        
766
        if(!queryFromQueryGroup.trim().equals("")){
767
            self.append(" docid IN (");
768
            self.append(queryFromQueryGroup);
769
            self.append(") ");
770
        }
771

    
772
        // Add SQL to filter for doctypes requested in the query
773
        // This is an implicit OR for the list of doctypes. Only doctypes in
774
        // this
775
        // list will be searched if the tag is present
776
        if (!filterDocList.isEmpty()) {
777
            boolean firstdoctype = true;
778
            boolean emptyString = true;
779

    
780
            if(!self.toString().equals("")){
781
                self.append(" AND (");
782
                emptyString = false;
783
            }
784

    
785
            Enumeration en = filterDocList.elements();
786
            while (en.hasMoreElements()) {
787
                String currentDoctype = (String) en.nextElement();
788
                if (firstdoctype) {
789
                    firstdoctype = false;
790
                    self.append(" doctype = '" + currentDoctype + "'");
791
                } else {
792
                    self.append(" OR doctype = '" + currentDoctype + "'");
793
                }
794
            }
795

    
796
            if(!emptyString){
797
                self.append(") ");
798
            }
799
        }
800

    
801
        // Add SQL to filter for owners requested in the query
802
        // This is an implicit OR for the list of owners
803
        if (!ownerList.isEmpty()) {
804
            boolean first = true;
805
            boolean emptyString = true;
806

    
807
            if(!self.toString().equals("")){
808
                self.append(" AND (");
809
                emptyString = false;
810
            }
811

    
812
            Enumeration en = ownerList.elements();
813
            while (en.hasMoreElements()) {
814
                String current = (String) en.nextElement();
815
                if (current != null) {
816
                    current = current.toLowerCase();
817
                }
818
                if (first) {
819
                    first = false;
820
                    self.append(" lower(user_owner) = '" + current + "'");
821
                } else {
822
                    self.append(" OR lower(user_owner) = '" + current + "'");
823
                }
824
            }
825

    
826
            if(!emptyString){
827
                self.append(") ");
828
            }
829
        }
830

    
831
        // if there is only one percentage search item, this query is a
832
        // percentage
833
        // search query
834
        logMetacat.info("percentage number: "
835
                + query.getPercentageSymbolCount());
836
        if (query.getPercentageSymbolCount() == 1) {
837
            logMetacat.info("It is a percentage search");
838
            percentageSearch = true;
839
        }
840

    
841
        queryString.append(self.toString());
842
        return queryString.toString();
843
    }
844

    
845
   
846

    
847
    /**
848
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
849
     * pathquery document. This allows for customization of the returned fields.
850
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
851
     * xml_nodes to find the fields to be included by their path expression, and
852
     * avoids the use of the xml_index table.
853
     *
854
     * @param doclist the list of document ids to search
855
     * @param unaccessableNodePair the node pairs (start id and end id) which
856
     *            this user should not access
857
     * @param useXMLIndex a boolean flag indicating whether to search using
858
     *            xml_index
859
     */
860
    public String printExtendedSQL(String doclist, boolean useXMLIndex)
861
    {
862
        if (useXMLIndex && !containsPredicates)
863
        {
864
            return printExtendedSQL(doclist);
865
        }
866
        else
867
        {
868
            StringBuffer self = new StringBuffer();
869
            boolean firstfield = true;
870
            // first part comes from fields without  predicates 
871
            String queryFromWithoutPrecidates = printExtendedSQL(doclist);
872
             if (queryFromWithoutPrecidates != null)
873
             {
874
            	 // it has return fields without predicate
875
            	 self.append(queryFromWithoutPrecidates);
876
            	 firstfield = false;
877
             }
878
            //put the returnfields into the query
879
            //the for loop allows for multiple fields
880
            for (int i = 0; i <   returnFieldListWithPredicates.size(); i++)
881
            {
882
                if (firstfield)
883
                {
884
                    firstfield = false;
885
                }
886
                else
887
                {
888
                    self.append(" UNION ");
889
                }
890
                String path  = (String)  returnFieldListWithPredicates.elementAt(i);
891
                self.append("select xml_nodes.docid, ");
892
                self.append("'"+ path.replaceAll("'", "''") + "' as path, ");
893
                self.append("xml_nodes.nodedata, ");
894
                self.append("xml_nodes.parentnodeid, ");
895
                self.append("xml_nodes.nodetype ");
896
                //self.append("from xml_nodes, xml_documents ");
897
                self.append("from xml_nodes ");
898
                self.append("where ");
899
                self.append(QueryTerm.useNestedStatements(path));
900

    
901
                self.append(" AND xml_nodes.docid in (");
902
                self.append(doclist);
903
                if (returnFieldIsAttribute(path))
904
                {
905
                    self.append(")");
906
                }
907
                else
908
                {
909
                     self.append(") AND xml_nodes.nodetype = 'TEXT'");
910
                }
911
                //self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
912

    
913
                //addAccessRestrictionSQL(unaccessableNodePair, self);
914
            }
915

    
916
            return self.toString();
917
        }
918
    }
919
    
920
    /*
921
     * Determines the returnfield is an attribute of not. 
922
     * For given returnfield, this programm will cut the part of path after last slash.
923
     * If no slash in the path, the original string will be considered as last part.
924
     * If first character of last part is @ it will retrun true. 
925
     */
926
    private boolean returnFieldIsAttribute(String path)
927
    {
928
    	boolean isAttribute = false;
929
    	if (path != null)
930
    	{
931
    	    int slashIndex = path.lastIndexOf("/");
932
    	    if (slashIndex !=-1)
933
    	    {
934
    	    	// if there is slash in the path, path should be replace by the last part
935
    	    	path = path.substring(slashIndex+1);
936
    	    }
937
    	    logMetacat.debug("In QuerySpecification.returnFieldIsAttribute method, final path is "+path);
938
    	    // if first of character of path is @, the path is attribute
939
    	    if (path.charAt(0) == '@')
940
    	    {
941
    	    	logMetacat.debug("it is attribute");
942
    	    	isAttribute = true;
943
    	    }
944
    	}
945
    	return isAttribute;
946
    }
947

    
948
    /**
949
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
950
     * pathquery document. This allows for customization of the returned fields.
951
     * It uses the xml_index table and so assumes that this table has been
952
     * built.
953
     *
954
     * @param doclist the list of document ids to search
955
     * @param unaccessableNodePair the node pairs (start id and end id)
956
     *            which this user should not access
957
     */
958
    private String printExtendedSQL(String doclist)
959
    {
960
        logMetacat.info("querySpecification.printExtendedSQL called\n");
961
        StringBuffer self = new StringBuffer();
962
        Vector elementVector = new Vector();
963
        Vector attributeVector = new Vector();
964

    
965
        boolean usePathIndex = true;
966

    
967
        // test if the are elements in the return fields
968
        if ( returnFieldList.size() == 0 ) {
969
            return null;
970
        }
971

    
972
        for (int i = 0; i < returnFieldList.size(); i++) {
973
        	String path = (String)returnFieldList.elementAt(i);
974
        	// Since return fileds having preicates will be handle in another path,
975
        	// we should skip it.
976
        	if (returnFieldListWithPredicates.contains(path))
977
        	{
978
        		continue;
979
        	}
980
        	if (path != null && path.indexOf(ATTRIBUTESYMBOL) != -1)
981
        	{
982
        		attributeVector.add(path);
983
        	}
984
        	else 
985
        	{
986
        		elementVector.add(path);
987
        	}       	
988
            if(!MetaCatUtil.pathsForIndexing.contains(path)){
989
                usePathIndex = false;              
990
            }
991
         
992
        }
993
        // check if has return field
994
        if (elementVector.size() == 0 && attributeVector.size()==0)
995
        {
996
        	return null;
997
        }
998

    
999
        if(usePathIndex){
1000
            self.append("select docid, path, nodedata, parentnodeid, null as nodetype ");
1001
            self.append("from xml_path_index where path in( '");
1002

    
1003
            boolean firstfield = true;
1004
            //put the returnfields into the query
1005
            //the for loop allows for multiple fields
1006
            for (int i = 0; i < returnFieldList.size(); i++) {
1007
                if (firstfield) {
1008
                    firstfield = false;
1009
                    self.append( (String) returnFieldList.elementAt(i));
1010
                    self.append("' ");
1011
                }
1012
                else {
1013
                    self.append(", '");
1014
                    self.append( (String) returnFieldList.elementAt(i));
1015
                    self.append("' ");
1016
                }
1017
            }
1018
            self.append(") AND docid in (");
1019
            self.append(doclist);
1020
            self.append(")");
1021

    
1022
        } else {
1023
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1024
            self.append("xml_nodes.parentnodeid, ");
1025
            self.append("xml_nodes.nodetype ");
1026
            self.append("FROM xml_index, xml_nodes WHERE (");
1027
           
1028

    
1029
            boolean firstElement = true;
1030
            boolean firstAttribute = true;
1031
            //put the returnfields into the query
1032
            //the for loop allows for multiple fields
1033
            if (elementVector.size() != 0)
1034
            {
1035
	            for (int i = 0; i < elementVector.size(); i++) {
1036
	            	String path = (String) elementVector.elementAt(i);
1037
	                if (firstElement) {
1038
	                	firstElement = false;
1039
	                	self.append(" (xml_index.nodeid=xml_nodes.parentnodeid AND xml_index.path IN ('");
1040
	                    self.append(path);
1041
	                    self.append("'");
1042
	                 }
1043
	                else 
1044
	                {
1045
	                    self.append(", '");
1046
	                    self.append(path);
1047
	                    self.append("' ");
1048
	                }
1049
	            }
1050
	            self.append(") AND xml_nodes.nodetype = 'TEXT')");
1051
            }
1052
            
1053
            if (attributeVector.size() != 0)
1054
            {
1055
            	for (int j=0; j<attributeVector.size(); j++)
1056
            	{
1057
            		String path = (String) attributeVector.elementAt(j);
1058
            		if (firstAttribute)
1059
            		{
1060
            			firstAttribute = false;
1061
            			if (!firstElement)
1062
                		{
1063
                			self.append(" OR ");
1064
                		}
1065
            			self.append(" (xml_index.nodeid=xml_nodes.nodeid AND ( xml_index.path IN ( '");
1066
	                    self.append(path);
1067
	                    self.append("'");
1068
            		}
1069
            		else 
1070
	                {
1071
	                    self.append(", '");
1072
	                    self.append(path);
1073
	                    self.append("' ");
1074
	                }
1075
            	}
1076
            	self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'))");
1077
            }
1078
            
1079
          
1080
            self.append(") AND xml_nodes.docid in (");
1081
            self.append(doclist);
1082
            self.append(")");
1083

    
1084
        }
1085

    
1086
        return self.toString();
1087
    }
1088

    
1089

    
1090
    /**
1091
     * Method to return a String generated after sorting the returnFieldList
1092
     * Vector
1093
     */
1094
    public String getSortedReturnFieldString(){
1095
        String returnFields = "";
1096

    
1097
        // Create a temporary vector and copy returnFieldList into it
1098
        Vector tempVector = new Vector();
1099

    
1100
        Iterator it = returnFieldList.iterator();
1101
        while(it.hasNext()){
1102
            tempVector.add(it.next());
1103
        }
1104

    
1105
        /*Enumeration attEnum = attributeReturnList.elements();
1106
        while(attEnum.hasMoreElements()){
1107
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1108
	    String rfield = "";
1109
            if(tempIt.hasNext()){
1110
		String element = (String)tempIt.next();
1111
		if(element != null) {
1112
		    rfield +=element;
1113
		}
1114
	    }
1115
            if(tempIt.hasNext()){
1116
		String attribute = (String)tempIt.next();
1117
		if(attribute != null) {
1118
  		    rfield = rfield + "@" + attribute;
1119
                }
1120
	    }
1121
            tempVector.add(rfield);
1122
        }*/
1123

    
1124
        // Sort the temporary vector
1125
        java.util.Collections.sort(tempVector);
1126

    
1127
        // Generate the string and return it
1128
        it = tempVector.iterator();
1129
        while(it.hasNext()){
1130
            returnFields = returnFields + it.next() + "|";
1131
        }
1132
        return returnFields;
1133
    }
1134

    
1135

    
1136
  
1137

    
1138

    
1139
    public static String printRelationSQL(String docid)
1140
    {
1141
        StringBuffer self = new StringBuffer();
1142
        self.append("select subject, relationship, object, subdoctype, ");
1143
        self.append("objdoctype from xml_relation ");
1144
        self.append("where docid like '").append(docid).append("'");
1145
        return self.toString();
1146
    }
1147

    
1148
    public static String printGetDocByDoctypeSQL(String docid)
1149
    {
1150
        StringBuffer self = new StringBuffer();
1151

    
1152
        self.append("SELECT docid,docname,doctype,");
1153
        self.append("date_created, date_updated ");
1154
        self.append("FROM xml_documents WHERE docid IN (");
1155
        self.append(docid).append(")");
1156
        return self.toString();
1157
    }
1158

    
1159
    /**
1160
     * create a String description of the query that this instance represents.
1161
     * This should become a way to get the XML serialization of the query.
1162
     */
1163
    public String toString()
1164
    {
1165
        return "meta_file_id=" + meta_file_id + "\n" + query;
1166
        //DOCTITLE attr cleared from the db
1167
        //return "meta_file_id=" + meta_file_id + "\n" +
1168
        //"querytitle=" + querytitle + "\n" + query;
1169
    }
1170

    
1171
    /** A method to get rid of attribute part in path expression */
1172
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1173
    {
1174
        if (pathExpression == null) { return null; }
1175
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1176
        String newExpression = null;
1177
        if (index != 0) {
1178
            newExpression = pathExpression.substring(0, index - 1);
1179
        }
1180
        logMetacat.info("The path expression without attributes: "
1181
                + newExpression);
1182
        return newExpression;
1183
    }
1184

    
1185
    /** A method to get attribute name from path */
1186
    public static String getAttributeName(String path)
1187
    {
1188
        if (path == null) { return null; }
1189
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1190
        int size = path.length();
1191
        String attributeName = null;
1192
        if (index != 1) {
1193
            attributeName = path.substring(index + 1, size);
1194
        }
1195
        logMetacat.info("The attirbute name from path: "
1196
                + attributeName);
1197
        return attributeName;
1198
    }
1199

    
1200
}
(56-56/68)