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: tao $'
12
 *     '$Date: 2007-07-12 16:56:17 -0700 (Thu, 12 Jul 2007) $'
13
 * '$Revision: 3308 $'
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.Hashtable;
37
import java.util.Stack;
38
import java.util.Vector;
39

    
40
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
41

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

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

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

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

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

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

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

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

    
79
    /** List of fields to be returned in result set */
80
    private Vector returnFieldList;
81

    
82
    /** List of users owning documents to be searched */
83
    private Vector ownerList;
84

    
85
    /** The root query group that contains the recursive query constraints */
86
    private QueryGroup query = null;
87

    
88
    // Query data structures used temporarily during XML parsing
89
    private Stack elementStack;
90

    
91
    private Stack queryStack;
92

    
93
    private String currentValue;
94

    
95
    private String currentPathexpr;
96

    
97
    private String parserName = null;
98

    
99
    private String accNumberSeparator = null;
100

    
101
    private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
102

    
103
    private boolean percentageSearch = false;
104

    
105
    private String userName = null;
106

    
107
    private static final String PUBLIC = "public";
108

    
109
    private String[] group = null;
110

    
111
    public static final String ATTRIBUTESYMBOL = "@";
112

    
113
    public static final char PREDICATE_START = '[';
114

    
115
    public static final char PREDICATE_END = ']';
116

    
117
    //private boolean hasAttributeReturnField = false;
118

    
119
    //private Hashtable attributeReturnList = new Hashtable();
120

    
121
    //private int countAttributeReturnField = 0;
122

    
123
    private StringBuffer textBuffer = new StringBuffer();
124
    
125
   
126
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
127

    
128
    /**
129
     * construct an instance of the QuerySpecification class
130
     *
131
     * @param queryspec
132
     *            the XML representation of the query (should conform to
133
     *            pathquery.dtd) as a Reader
134
     * @param parserName
135
     *            the fully qualified name of a Java Class implementing the
136
     *            org.xml.sax.XMLReader interface
137
     */
138
    public QuerySpecification(Reader queryspec, String parserName,
139
            String accNumberSeparator) throws IOException
140
    {
141
        super();
142

    
143
        // Initialize the class variables
144
        returnDocList = new Vector();
145
        filterDocList = new Vector();
146
        elementStack = new Stack();
147
        queryStack = new Stack();
148
        returnFieldList = new Vector();
149
        ownerList = new Vector();
150
        this.parserName = parserName;
151
        this.accNumberSeparator = accNumberSeparator;
152

    
153
        // Initialize the parser and read the queryspec
154
        XMLReader parser = initializeParser();
155
        if (parser == null) {
156
            System.err.println("SAX parser not instantiated properly.");
157
        }
158
        try {
159
            parser.parse(new InputSource(queryspec));
160
        } catch (SAXException e) {
161
            System.err.println("error parsing data in "
162
                    + "QuerySpecification.QuerySpecification");
163
            System.err.println(e.getMessage());
164
        }
165
    }
166

    
167
    /**
168
     * construct an instance of the QuerySpecification class
169
     *
170
     * @param queryspec
171
     *            the XML representation of the query (should conform to
172
     *            pathquery.dtd) as a String
173
     * @param parserName
174
     *            the fully qualified name of a Java Class implementing the
175
     *            org.xml.sax.Parser interface
176
     */
177
    public QuerySpecification(String queryspec, String parserName,
178
            String accNumberSeparator) throws IOException
179
    {
180
        this(new StringReader(queryspec), parserName, accNumberSeparator);
181
    }
182

    
183
    /**
184
     * construct an instance of the QuerySpecification class which don't need
185
     * to parser a xml document
186
     *
187
     * @param accNumberSeparator
188
     *            the separator between doc version
189
     */
190
    public QuerySpecification(String accNumberSeparator) throws IOException
191
    {
192
        // Initialize the class variables
193
        returnDocList = new Vector();
194
        filterDocList = new Vector();
195
        elementStack = new Stack();
196
        queryStack = new Stack();
197
        returnFieldList = new Vector();
198
        ownerList = new Vector();
199
        this.accNumberSeparator = accNumberSeparator;
200
    }
201

    
202
    /**
203
     * Method to set user name
204
     *
205
     * @param myName
206
     *            the user name
207
     */
208
    public void setUserName(String myName)
209
    {
210
        //to lower case
211
        if (myName != null) {
212
            this.userName = myName.toLowerCase();
213
        } else {
214
            this.userName = myName;
215
        }
216
    }
217

    
218
    /**
219
     * Method to set user group
220
     *
221
     * @param myGroup
222
     *            the user group
223
     */
224
    public void setGroup(String[] myGroup)
225
    {
226
        this.group = myGroup;
227
    }
228

    
229
    /**
230
     * Method to indicate this query is a percentage search
231
     */
232
    public boolean isPercentageSearch()
233
    {
234
        return percentageSearch;
235
    }
236

    
237
    /*
238
     * Method to get owner query. If it is owner it has all permission
239
     */
240
    private String createOwerQuery()
241
    {
242
        String ownerQuery = null;
243
        //if user is public, we don't need to run owner query
244
        if (userName != null && !userName.equalsIgnoreCase(PUBLIC))
245
        {
246
	        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
247
	        if (userName != null && !userName.equals("")) {
248
	            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
249
	        }
250
        }
251
        logMetacat.info("OwnerQuery: " + ownerQuery);
252
        return ownerQuery;
253
    }
254

    
255
    /*
256
     * Method to create query for xml_access, this part is to get docid list
257
     * which have a allow rule for a given user
258
     */
259
    private String createAllowRuleQuery()
260
    {
261
        String allowQuery = null;
262
        String allowString = constructAllowString();
263
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
264
        allowQuery = allowQuery + ") AND subtreeid IS NULL";
265
        logMetacat.info("allow query is: " + allowQuery);
266
        return allowQuery;
267

    
268
    }
269

    
270
    /* Method to construct a allow rule string */
271
    private String constructAllowString()
272
    {
273
        String allowQuery = "";
274
        // add allow rule for user name
275
        if (userName != null && !userName.equals("")) {
276
            allowQuery = allowQuery + "(lower(principal_name) = '" + userName
277
                    + "' AND perm_type = 'allow'"
278
                    + " AND (permission='4' OR permission='5' " 
279
                    + "OR permission='6' OR permission='7'))";
280
        }
281
        // add allow rule for public
282
        allowQuery = allowQuery + "OR (lower(principal_name) = '" + PUBLIC
283
                + "' AND perm_type = 'allow'"
284
                + " AND (permission='4' OR permission='5' " 
285
                + "OR permission='6' OR permission='7'))";
286

    
287
        // add allow rule for group
288
        if (group != null) {
289
            for (int i = 0; i < group.length; i++) {
290
                String groupUint = group[i];
291
                if (groupUint != null && !groupUint.equals("")) {
292
                    groupUint = groupUint.toLowerCase();
293
                    allowQuery = allowQuery + " OR (lower(principal_name) = '"
294
                            + groupUint + "' AND perm_type = 'allow'"
295
                	    + " AND (permission='4' OR permission='5' " 
296
                            + "OR permission='6' OR permission='7'))";
297
                }//if
298
            }//for
299
        }//if
300
        logMetacat.info("allow string is: " + allowQuery);
301
        return allowQuery;
302
    }
303

    
304
    /*
305
     * Method to create query for xml_access, this part is to get docid list
306
     * which have a deny rule and perm_order is allowFirst for a given user.
307
     * This means the user will be denied to read
308
     */
309
    private String createDenyRuleQuery()
310
    {
311
        String denyQuery = null;
312
        String denyString = constructDenyString();
313
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
314
        denyQuery = denyQuery + ") AND subtreeid IS NULL ";
315
        logMetacat.info("denyquery is: " + denyQuery);
316
        return denyQuery;
317

    
318
    }
319

    
320
    /* Construct deny string */
321
    private String constructDenyString()
322
    {
323
        String denyQuery = "";
324
        // add deny rule for user name
325
        if (userName != null && !userName.equals("")) {
326
            denyQuery = denyQuery + "(lower(principal_name) = '" + userName
327
                    + "' AND perm_type = 'deny' "
328
                    + "AND perm_order ='allowFirst'"
329
                    + " AND (permission='4' OR permission='5' " 
330
                    + "OR permission='6' OR permission='7'))";
331
        }
332
        // add deny rule for public
333
        denyQuery = denyQuery + "OR (lower(principal_name) = '" + PUBLIC
334
                + "' AND perm_type = 'deny' " + "AND perm_order ='allowFirst'"
335
                + " AND (permission='4' OR permission='5' " 
336
                + "OR permission='6' OR permission='7'))";
337

    
338
        // add allow rule for group
339
        if (group != null) {
340
            for (int i = 0; i < group.length; i++) {
341
                String groupUint = group[i];
342
                if (groupUint != null && !groupUint.equals("")) {
343
                    groupUint = groupUint.toLowerCase();
344
                    denyQuery = denyQuery + " OR (lower(principal_name) = '"
345
                            + groupUint + "' AND perm_type = 'deny' "
346
                            + "AND perm_order ='allowFirst'"
347
                	    + " AND (permission='4' OR permission='5' " 
348
                            + "OR permission='6' OR permission='7'))";
349
                }//if
350
            }//for
351
        }//if
352
        return denyQuery;
353
    }
354

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

    
383
    /**
384
     * Returns true if the parsed query contains and extended xml query (i.e.
385
     * there is at least one &lt;returnfield&gt; in the pathquery document)
386
     */
387
    public boolean containsExtendedSQL()
388
    {
389
        if (containsExtendedSQL) {
390
            return true;
391
        } else {
392
            return false;
393
        }
394
    }
395

    
396
  
397
    /**
398
     * Accessor method to return the identifier of this Query
399
     */
400
    public String getIdentifier()
401
    {
402
        return meta_file_id;
403
    }
404

    
405
    /**
406
     * method to set the identifier of this query
407
     */
408
    public void setIdentifier(String id)
409
    {
410
        this.meta_file_id = id;
411
    }
412

    
413
    /**
414
     * Accessor method to return the title of this Query
415
     */
416
    public String getQueryTitle()
417
    {
418
        return queryTitle;
419
    }
420

    
421
    /**
422
     * method to set the title of this query
423
     */
424
    public void setQueryTitle(String title)
425
    {
426
        this.queryTitle = title;
427
    }
428

    
429
    /**
430
     * Accessor method to return a vector of the return document types as
431
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
432
     */
433
    public Vector getReturnDocList()
434
    {
435
        return this.returnDocList;
436
    }
437

    
438
    /**
439
     * method to set the list of return docs of this query
440
     */
441
    public void setReturnDocList(Vector returnDocList)
442
    {
443
        this.returnDocList = returnDocList;
444
    }
445

    
446
    /**
447
     * Accessor method to return a vector of the filter doc types as defined in
448
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
449
     */
450
    public Vector getFilterDocList()
451
    {
452
        return this.filterDocList;
453
    }
454

    
455
    /**
456
     * method to set the list of filter docs of this query
457
     */
458
    public void setFilterDocList(Vector filterDocList)
459
    {
460
        this.filterDocList = filterDocList;
461
    }
462

    
463
    /**
464
     * Accessor method to return a vector of the extended return fields as
465
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
466
     */
467
    public Vector getReturnFieldList()
468
    {
469
        return this.returnFieldList;
470
    }
471

    
472
    /**
473
     * method to set the list of fields to be returned by this query
474
     */
475
    public void setReturnFieldList(Vector returnFieldList)
476
    {
477
        this.returnFieldList = returnFieldList;
478
    }
479

    
480
    /**
481
     * Accessor method to return a vector of the owner fields as defined in the
482
     * &lt;owner&gt; tag in the pathquery dtd.
483
     */
484
    public Vector getOwnerList()
485
    {
486
        return this.ownerList;
487
    }
488

    
489
    /**
490
     * method to set the list of owners used to constrain this query
491
     */
492
    public void setOwnerList(Vector ownerList)
493
    {
494
        this.ownerList = ownerList;
495
    }
496

    
497
    /**
498
     * get the QueryGroup used to express query constraints
499
     */
500
    public QueryGroup getQueryGroup()
501
    {
502
        return query;
503
    }
504

    
505
    /**
506
     * set the querygroup
507
     */
508
    public void setQueryGroup(QueryGroup group)
509
    {
510
        query = group;
511
    }
512

    
513
    /**
514
     * set if this query sepcification has extendQuery(has return doc type or
515
     * not)
516
     */
517
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
518
    {
519
        containsExtendedSQL = hasExtenedQuery;
520
    }
521

    
522
    /**
523
     * Set up the SAX parser for reading the XML serialized query
524
     */
525
    private XMLReader initializeParser()
526
    {
527
        XMLReader parser = null;
528

    
529
        // Set up the SAX document handlers for parsing
530
        try {
531

    
532
            // Get an instance of the parser
533
            parser = XMLReaderFactory.createXMLReader(parserName);
534

    
535
            // Set the ContentHandler to this instance
536
            parser.setContentHandler(this);
537

    
538
            // Set the error Handler to this instance
539
            parser.setErrorHandler(this);
540

    
541
        } catch (Exception e) {
542
            System.err.println("Error in QuerySpcecification.initializeParser "
543
                    + e.toString());
544
        }
545

    
546
        return parser;
547
    }
548

    
549
    /**
550
     * callback method used by the SAX Parser when the start tag of an element
551
     * is detected. Used in this context to parse and store the query
552
     * information in class variables.
553
     */
554
    public void startElement(String uri, String localName, String qName,
555
            Attributes atts) throws SAXException
556
    {
557
        BasicNode currentNode = new BasicNode(localName);
558
        // add attributes to BasicNode here
559
        if (atts != null) {
560
            int len = atts.getLength();
561
            for (int i = 0; i < len; i++) {
562
                currentNode
563
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
564
            }
565
        }
566

    
567
        elementStack.push(currentNode);
568
        if (currentNode.getTagName().equals("querygroup")) {
569
            QueryGroup currentGroup = new QueryGroup(currentNode
570
                    .getAttribute("operator"));
571
            if (query == null) {
572
                query = currentGroup;
573
            } else {
574
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
575
                parentGroup.addChild(currentGroup);
576
            }
577
            queryStack.push(currentGroup);
578
        }
579
    }
580

    
581
    /**
582
     * callback method used by the SAX Parser when the end tag of an element is
583
     * detected. Used in this context to parse and store the query information
584
     * in class variables.
585
     */
586
    public void endElement(String uri, String localName, String qName)
587
            throws SAXException
588
    {
589
        BasicNode leaving = (BasicNode) elementStack.pop();
590
        if (leaving.getTagName().equals("queryterm")) {
591
            boolean isCaseSensitive = (new Boolean(leaving
592
                    .getAttribute("casesensitive"))).booleanValue();
593
            QueryTerm currentTerm = null;
594
            if (currentPathexpr == null) {
595
                currentTerm = new QueryTerm(isCaseSensitive, leaving
596
                        .getAttribute("searchmode"), currentValue);
597
            } else {
598
                currentTerm = new QueryTerm(isCaseSensitive, leaving
599
                        .getAttribute("searchmode"), currentValue,
600
                        currentPathexpr);
601
            }
602
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
603
            currentGroup.addChild(currentTerm);
604
            currentValue = null;
605
            currentPathexpr = null;
606
        } else if (leaving.getTagName().equals("querygroup")) {
607
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
608
        } else if (leaving.getTagName().equals("meta_file_id")) {
609
              meta_file_id = textBuffer.toString().trim();
610
        } else if (leaving.getTagName().equals("querytitle")) {
611
              queryTitle = textBuffer.toString().trim();
612
        } else if (leaving.getTagName().equals("value")) {
613
              currentValue = textBuffer.toString().trim();
614
        } else if (leaving.getTagName().equals("pathexpr")) {
615
              currentPathexpr = textBuffer.toString().trim();
616
        } else if (leaving.getTagName().equals("returndoctype")) {
617
              returnDocList.add(textBuffer.toString().trim());
618
        } else if (leaving.getTagName().equals("filterdoctype")) {
619
              filterDocList.add(textBuffer.toString().trim());
620
        } else if (leaving.getTagName().equals("returnfield")) {
621
              handleReturnField(textBuffer.toString().trim());
622
        } else if (leaving.getTagName().equals("filterdoctype")) {
623
              filterDocList.add(textBuffer.toString().trim());
624
        } else if (leaving.getTagName().equals("owner")) {
625
              ownerList.add(textBuffer.toString().trim());
626
        }
627

    
628
        //rest textBuffer
629
        textBuffer = new StringBuffer();
630

    
631
    }
632

    
633
    /**
634
     * callback method used by the SAX Parser when the text sequences of an xml
635
     * stream are detected. Used in this context to parse and store the query
636
     * information in class variables.
637
     */
638
    public void characters(char ch[], int start, int length)
639
    {
640
      // buffer all text nodes for same element. This is for text was splited
641
      // into different nodes
642
      textBuffer.append(new String(ch, start, length));
643

    
644
    }
645

    
646
    /**
647
     * Method to transfer string to return field
648
     */
649
    /* public void handleReturnField(String inputString)
650
    {
651
        // make sure if return fields has an attribute or not
652
        if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
653
            // no attribute value will be returned
654
            logMetacat.info("QuerySpecification.handleReturnField(): " );
655
            logMetacat.info("  there are no attributes in the XPATH statement" );
656
            returnFieldList.add(inputString);
657
            containsExtendedSQL = true;
658
        } else {
659

    
660
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
661

    
662
            // case where the return field is solely an attribute
663
            logMetacat.info("QuerySpecification.handleReturnField(): " );
664
            logMetacat.info("  there are *only* attributes in the XPATH statement" );
665
            String returnPath = newPathExpressionWithOutAttribute(inputString);
666
            String attributeName = getAttributeName(inputString);
667
            Vector pathInfo = new Vector();
668
            // the vector has the information about return path and
669
            // attributename
670
            pathInfo.addElement(returnPath);
671
            pathInfo.addElement(attributeName);
672
            // put the vector into a hash table. The reseaon why don't put
673
            // return path or attributename as a key is because they are not
674
            // unique
675
            attributeReturnList.put(new Integer(countAttributeReturnField),
676
                    pathInfo);
677
            countAttributeReturnField++;
678
            hasAttributeReturnField = true;
679
            containsExtendedSQL = true;
680
          } else {
681
            // has a attribute return field
682
            // divied the return filed into two parts, one is path and the
683
            // other is attribue name
684
            logMetacat.info("QuerySpecification.handleReturnField: " );
685
            logMetacat.info("  there are both attributes and elements" );
686
            logMetacat.info("  in the XPATH statement" );
687
            String returnPath = newPathExpressionWithOutAttribute(inputString);
688
            String attributeName = getAttributeName(inputString);
689
            Vector pathInfo = new Vector();
690
            // the vector has the information about return path and
691
            // attributename
692
            pathInfo.addElement(returnPath);
693
            pathInfo.addElement(attributeName);
694
            // put the vector into a hash table. The reseaon why don't put
695
            // return path or attributename as a key is because they are not
696
            // unique
697
            attributeReturnList.put(new Integer(countAttributeReturnField),
698
                    pathInfo);
699
            countAttributeReturnField++;
700
            hasAttributeReturnField = true;
701
            containsExtendedSQL = true;
702
          }
703
        }
704
    }
705
     */
706
    private void handleReturnField(String inputString)
707
    {
708
        int attributePos = inputString.indexOf(ATTRIBUTESYMBOL);
709
        int predicateStart = -1;
710
        int predicateEnd;
711
        boolean hasPredicate = false;
712

    
713
        while (true)
714
        {
715
            predicateStart = inputString.indexOf(PREDICATE_START, predicateStart + 1);
716

    
717
            if (attributePos == -1)
718
                break;
719

    
720
            if (predicateStart == -1)
721
                break;
722

    
723
            hasPredicate = true;
724

    
725
            if (attributePos < predicateStart)
726
                break;
727

    
728
            predicateEnd = inputString.indexOf(PREDICATE_END, predicateStart);
729

    
730
            if (predicateEnd == -1)
731
            {
732
                logMetacat.warn("handleReturnField(): ");
733
                logMetacat.warn("    Invalid path: " + inputString);
734
                return;
735
            }
736

    
737
            while (attributePos < predicateEnd)
738
            {
739
                attributePos = inputString.indexOf(ATTRIBUTESYMBOL, attributePos + 1);
740

    
741
                if (attributePos == -1)
742
                    break;
743
            }
744
        }
745

    
746
        if (hasPredicate)
747
            containsPredicates = true;
748

    
749
        containsExtendedSQL = true;
750

    
751
     
752
            // no attribute value will be returned
753
            logMetacat.info("QuerySpecification.handleReturnField(): " );
754
            logMetacat.info("  there are no attributes in the XPATH statement" );
755
            returnFieldList.add(inputString);
756
       
757

    
758
       
759
    }
760

    
761
    /**
762
     * create a SQL serialization of the query that this instance represents
763
     */
764
    public String printSQL(boolean useXMLIndex)
765
    {
766

    
767
        StringBuffer self = new StringBuffer();
768
        StringBuffer queryString = new StringBuffer();
769

    
770
        queryString.append("SELECT docid,docname,doctype,");
771
        queryString.append("date_created, date_updated, rev ");
772
        queryString.append("FROM xml_documents WHERE");
773

    
774
        // Get the query from the QueryGroup and check
775
        // if no query has been returned
776
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
777
        logMetacat.info("Query from query in QuerySpec.printSQL: " 
778
        		+ queryFromQueryGroup);
779
        
780
        if(!queryFromQueryGroup.trim().equals("")){
781
            self.append(" docid IN (");
782
            self.append(queryFromQueryGroup);
783
            self.append(") ");
784
        }
785

    
786
        // Add SQL to filter for doctypes requested in the query
787
        // This is an implicit OR for the list of doctypes. Only doctypes in
788
        // this
789
        // list will be searched if the tag is present
790
        if (!filterDocList.isEmpty()) {
791
            boolean firstdoctype = true;
792
            boolean emptyString = true;
793

    
794
            if(!self.toString().equals("")){
795
                self.append(" AND (");
796
                emptyString = false;
797
            }
798

    
799
            Enumeration en = filterDocList.elements();
800
            while (en.hasMoreElements()) {
801
                String currentDoctype = (String) en.nextElement();
802
                if (firstdoctype) {
803
                    firstdoctype = false;
804
                    self.append(" doctype = '" + currentDoctype + "'");
805
                } else {
806
                    self.append(" OR doctype = '" + currentDoctype + "'");
807
                }
808
            }
809

    
810
            if(!emptyString){
811
                self.append(") ");
812
            }
813
        }
814

    
815
        // Add SQL to filter for owners requested in the query
816
        // This is an implicit OR for the list of owners
817
        if (!ownerList.isEmpty()) {
818
            boolean first = true;
819
            boolean emptyString = true;
820

    
821
            if(!self.toString().equals("")){
822
                self.append(" AND (");
823
                emptyString = false;
824
            }
825

    
826
            Enumeration en = ownerList.elements();
827
            while (en.hasMoreElements()) {
828
                String current = (String) en.nextElement();
829
                if (current != null) {
830
                    current = current.toLowerCase();
831
                }
832
                if (first) {
833
                    first = false;
834
                    self.append(" lower(user_owner) = '" + current + "'");
835
                } else {
836
                    self.append(" OR lower(user_owner) = '" + current + "'");
837
                }
838
            }
839

    
840
            if(!emptyString){
841
                self.append(") ");
842
            }
843
        }
844

    
845
        // if there is only one percentage search item, this query is a
846
        // percentage
847
        // search query
848
        logMetacat.info("percentage number: "
849
                + query.getPercentageSymbolCount());
850
        if (query.getPercentageSymbolCount() == 1) {
851
            logMetacat.info("It is a percentage search");
852
            percentageSearch = true;
853
        }
854

    
855
        queryString.append(self.toString());
856
        return queryString.toString();
857
    }
858

    
859
    /**
860
     * This sql command will selecet startnodeid and endnodeid that user can
861
     * NOT access
862
     */
863
    /*public String printAccessControlSQLForReturnField(String doclist)
864
    {
865
        StringBuffer sql = new StringBuffer();
866
        String allowString = constructAllowString();
867
        String denyString = constructDenyString();
868
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
869
        sql.append("WHERE docid in (");
870
        sql.append(doclist);
871
        sql.append(") AND startnodeid IS NOT NULL AND ");
872
        sql.append("(");
873
        sql.append("(");
874
        sql
875
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
876
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
877
        sql.append(" AND lower(xml_documents.user_owner) ='");
878
        sql.append(userName);
879
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
880
        sql.append(")");
881
        sql.append(" AND ");
882
        sql.append("(");
883
        sql
884
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
885
        sql.append(allowString);
886
        sql.append(") AND (startnodeid IS NOT NULL))");
887
        sql.append(")");
888
        sql
889
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
890
        sql.append(denyString);
891
        sql.append(") AND (startnodeid IS NOT NULL))");
892
        sql.append(")");
893
        sql.append(")");
894
        sql.append(")");
895
        logMetacat.info("accessControlSQLForReturnField: "
896
                + sql.toString());
897
        return sql.toString();
898
    }*/
899

    
900
    /**
901
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
902
     * pathquery document. This allows for customization of the returned fields.
903
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
904
     * xml_nodes to find the fields to be included by their path expression, and
905
     * avoids the use of the xml_index table.
906
     *
907
     * @param doclist the list of document ids to search
908
     * @param unaccessableNodePair the node pairs (start id and end id) which
909
     *            this user should not access
910
     * @param useXMLIndex a boolean flag indicating whether to search using
911
     *            xml_index
912
     */
913
    public String printExtendedSQL(String doclist, boolean useXMLIndex)
914
    {
915
        if (useXMLIndex && !containsPredicates)
916
        {
917
            return printExtendedSQL(doclist);
918
        }
919
        else
920
        {
921
            StringBuffer self = new StringBuffer();
922

    
923
            boolean firstfield = true;
924
            //put the returnfields into the query
925
            //the for loop allows for multiple fields
926
            for (int i = 0; i < returnFieldList.size(); i++)
927
            {
928
                if (firstfield)
929
                {
930
                    firstfield = false;
931
                }
932
                else
933
                {
934
                    self.append(" UNION ");
935
                }
936
                String path  = (String) returnFieldList.elementAt(i);
937
                self.append("select xml_nodes.docid, ");
938
                self.append("'"+ path.replaceAll("'", "''") + "' as path, xml_nodes.nodedata, ");
939
                self.append("xml_nodes.parentnodeid ");
940
                self.append("from xml_nodes, xml_documents ");
941
                self.append("where parentnodeid IN ");
942
                self.append(QueryTerm.useNestedStatements(path));
943

    
944
                self.append(" AND xml_nodes.docid in (");
945
                self.append(doclist);
946
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
947
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
948

    
949
                //addAccessRestrictionSQL(unaccessableNodePair, self);
950
            }
951

    
952
            return self.toString();
953
        }
954
    }
955

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

    
971
        boolean usePathIndex = true;
972

    
973
        // test if the are elements in the return fields
974
        if ( returnFieldList.size() == 0 ) {
975
            return null;
976
        }
977

    
978
        for (int i = 0; i < returnFieldList.size(); i++) {
979
            if(!MetaCatUtil.pathsForIndexing.contains
980
               (returnFieldList.elementAt(i))){
981
                usePathIndex = false;
982
                break;
983
            }
984
        }
985

    
986
        if(usePathIndex){
987
            self.append("select docid, path, nodedata, parentnodeid ");
988
            self.append("from xml_path_index where (path like '");
989

    
990
            boolean firstfield = true;
991
            //put the returnfields into the query
992
            //the for loop allows for multiple fields
993
            for (int i = 0; i < returnFieldList.size(); i++) {
994
                if (firstfield) {
995
                    firstfield = false;
996
                    self.append( (String) returnFieldList.elementAt(i));
997
                    self.append("' ");
998
                }
999
                else {
1000
                    self.append("or path like '");
1001
                    self.append( (String) returnFieldList.elementAt(i));
1002
                    self.append("' ");
1003
                }
1004
            }
1005
            self.append(") AND docid in (");
1006
            self.append(doclist);
1007
            self.append(")");
1008

    
1009
        } else {
1010
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1011
            self.append("xml_nodes.parentnodeid ");
1012
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
1013
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
1014

    
1015
            boolean firstfield = true;
1016
            //put the returnfields into the query
1017
            //the for loop allows for multiple fields
1018
            for (int i = 0; i < returnFieldList.size(); i++) {
1019
                if (firstfield) {
1020
                    firstfield = false;
1021
                    self.append((String) returnFieldList.elementAt(i));
1022
                    self.append("' ");
1023
                } else {
1024
                    self.append("or xml_index.path like '");
1025
                    self.append((String) returnFieldList.elementAt(i));
1026
                    self.append("' ");
1027
                }
1028
            }
1029
            self.append(") AND xml_nodes.docid in (");
1030
            self.append(doclist);
1031
            self.append(") AND xml_nodes.nodetype = 'TEXT'");
1032

    
1033
        }
1034

    
1035
        //addAccessRestrictionSQL(unaccessableNodePair, self);
1036

    
1037
        return self.toString();
1038
    }
1039

    
1040

    
1041
    /**
1042
     * Method to return a String generated after sorting the returnFieldList
1043
     * Vector
1044
     */
1045
    public String getSortedReturnFieldString(){
1046
        String returnFields = "";
1047

    
1048
        // Create a temporary vector and copy returnFieldList into it
1049
        Vector tempVector = new Vector();
1050

    
1051
        Iterator it = returnFieldList.iterator();
1052
        while(it.hasNext()){
1053
            tempVector.add(it.next());
1054
        }
1055

    
1056
        /*Enumeration attEnum = attributeReturnList.elements();
1057
        while(attEnum.hasMoreElements()){
1058
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1059
	    String rfield = "";
1060
            if(tempIt.hasNext()){
1061
		String element = (String)tempIt.next();
1062
		if(element != null) {
1063
		    rfield +=element;
1064
		}
1065
	    }
1066
            if(tempIt.hasNext()){
1067
		String attribute = (String)tempIt.next();
1068
		if(attribute != null) {
1069
  		    rfield = rfield + "@" + attribute;
1070
                }
1071
	    }
1072
            tempVector.add(rfield);
1073
        }*/
1074

    
1075
        // Sort the temporary vector
1076
        java.util.Collections.sort(tempVector);
1077

    
1078
        // Generate the string and return it
1079
        it = tempVector.iterator();
1080
        while(it.hasNext()){
1081
            returnFields = returnFields + it.next() + "|";
1082
        }
1083
        return returnFields;
1084
    }
1085

    
1086

    
1087
    /**
1088
     * Create the SQl necessary to restrict access to allowed nodes.  This is
1089
     * accomplished by restricting the nodes that are returned to include
1090
     * only those whose IDs fall outside of a set of start/stop pairs of
1091
     * nodeid values.  These pairs are passed in as a hash, with the key
1092
     * containing the start nodeid and the value containing the end nodeid.
1093
     * Any nodes between these start and end nodeid values will be excluded
1094
     * from the results.
1095
     *
1096
     * @param unaccessableNodePair hash of start/end nodeid pairs to restrict
1097
     * @param self a stringbuffer to which the genrated SQL is appended
1098
     */
1099
    /*private void addAccessRestrictionSQL(Hashtable unaccessableNodePair,
1100
            StringBuffer self)
1101
    {
1102
        // add control part for extended query
1103
        Enumeration en = unaccessableNodePair.keys();
1104

    
1105
        while (en.hasMoreElements()) {
1106
            // Get control pairs in object
1107
            Long startNodeIdObject = (Long) en.nextElement();
1108
            Long endNodeIdObject = (Long) unaccessableNodePair
1109
                    .get(startNodeIdObject);
1110
            // change it to long
1111
            long startNodeId = startNodeIdObject.longValue();
1112
            long endNodeId = endNodeIdObject.longValue();
1113
            // add into query
1114
            self.append(" AND ( xml_nodes.nodeid < ");
1115
            self.append(startNodeId);
1116
            self.append(" OR xml_nodes.nodeid > ");
1117
            self.append(endNodeId);
1118
            self.append(")");
1119
        }
1120
    }*/
1121

    
1122
   
1123

    
1124

    
1125

    
1126
    public static String printRelationSQL(String docid)
1127
    {
1128
        StringBuffer self = new StringBuffer();
1129
        self.append("select subject, relationship, object, subdoctype, ");
1130
        self.append("objdoctype from xml_relation ");
1131
        self.append("where docid like '").append(docid).append("'");
1132
        return self.toString();
1133
    }
1134

    
1135
    public static String printGetDocByDoctypeSQL(String docid)
1136
    {
1137
        StringBuffer self = new StringBuffer();
1138

    
1139
        self.append("SELECT docid,docname,doctype,");
1140
        self.append("date_created, date_updated ");
1141
        self.append("FROM xml_documents WHERE docid IN (");
1142
        self.append(docid).append(")");
1143
        return self.toString();
1144
    }
1145

    
1146
    /**
1147
     * create a String description of the query that this instance represents.
1148
     * This should become a way to get the XML serialization of the query.
1149
     */
1150
    public String toString()
1151
    {
1152
        return "meta_file_id=" + meta_file_id + "\n" + query;
1153
        //DOCTITLE attr cleared from the db
1154
        //return "meta_file_id=" + meta_file_id + "\n" +
1155
        //"querytitle=" + querytitle + "\n" + query;
1156
    }
1157

    
1158
    /** A method to get rid of attribute part in path expression */
1159
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1160
    {
1161
        if (pathExpression == null) { return null; }
1162
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1163
        String newExpression = null;
1164
        if (index != 0) {
1165
            newExpression = pathExpression.substring(0, index - 1);
1166
        }
1167
        logMetacat.info("The path expression without attributes: "
1168
                + newExpression);
1169
        return newExpression;
1170
    }
1171

    
1172
    /** A method to get attribute name from path */
1173
    public static String getAttributeName(String path)
1174
    {
1175
        if (path == null) { return null; }
1176
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1177
        int size = path.length();
1178
        String attributeName = null;
1179
        if (index != 1) {
1180
            attributeName = path.substring(index + 1, size);
1181
        }
1182
        logMetacat.info("The attirbute name from path: "
1183
                + attributeName);
1184
        return attributeName;
1185
    }
1186

    
1187
}
(54-54/66)