Project

General

Profile

1 155 jones
/**
2 203 jones
 *  '$RCSfile$'
3 2093 tao
 *    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 203 jones
 *             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 349 jones
 *    Release: @release@
11 155 jones
 *
12 203 jones
 *   '$Author$'
13
 *     '$Date$'
14
 * '$Revision$'
15 669 jones
 *
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 155 jones
 */
30
31
package edu.ucsb.nceas.metacat;
32
33 2067 jones
import java.io.IOException;
34
import java.io.Reader;
35
import java.io.StringReader;
36
import java.util.Enumeration;
37 1354 tao
import java.util.Hashtable;
38 155 jones
import java.util.Stack;
39 158 jones
import java.util.Vector;
40 155 jones
41 2067 jones
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
42
43 2663 sgarg
import org.apache.log4j.Logger;
44 185 jones
import org.xml.sax.Attributes;
45 158 jones
import org.xml.sax.InputSource;
46
import org.xml.sax.SAXException;
47 185 jones
import org.xml.sax.XMLReader;
48 2067 jones
import org.xml.sax.helpers.DefaultHandler;
49 185 jones
import org.xml.sax.helpers.XMLReaderFactory;
50 2419 sgarg
import java.util.Iterator;
51 155 jones
52 402 berkley
/**
53 2067 jones
 * A Class that represents a structured query, and can be constructed from an
54
 * XML serialization conforming to
55 2093 tao
 *
56 2067 jones
 * @see pathquery.dtd. The printSQL() method can be used to print a SQL
57
 *      serialization of the query.
58 155 jones
 */
59 2067 jones
public class QuerySpecification extends DefaultHandler
60
{
61 1832 tao
62 2067 jones
    /** flag determining whether extended query terms are present */
63
    private boolean containsExtendedSQL = false;
64 158 jones
65 2067 jones
    /** Identifier for this query document */
66
    private String meta_file_id;
67 158 jones
68 2067 jones
    /** Title of this query */
69
    private String queryTitle;
70
71
    /** List of document types to be returned using package back tracing */
72
    private Vector returnDocList;
73
74
    /** List of document types to be searched */
75
    private Vector filterDocList;
76
77
    /** List of fields to be returned in result set */
78
    private Vector returnFieldList;
79
80
    /** List of users owning documents to be searched */
81
    private Vector ownerList;
82
83
    /** The root query group that contains the recursive query constraints */
84
    private QueryGroup query = null;
85
86
    // Query data structures used temporarily during XML parsing
87
    private Stack elementStack;
88
89
    private Stack queryStack;
90
91
    private String currentValue;
92
93
    private String currentPathexpr;
94
95
    private String parserName = null;
96
97
    private String accNumberSeparator = null;
98
99
    private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
100
101
    private boolean percentageSearch = false;
102
103
    private String userName = null;
104
105
    private static final String PUBLIC = "public";
106
107
    private String[] group = null;
108
109
    public static final String ATTRIBUTESYMBOL = "@";
110
111
    private boolean hasAttributeReturnField = false;
112
113
    private Hashtable attributeReturnList = new Hashtable();
114
115
    private int countAttributeReturnField = 0;
116
117 2093 tao
    private StringBuffer textBuffer = new StringBuffer();
118
119 2663 sgarg
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
120
121 2067 jones
    /**
122
     * construct an instance of the QuerySpecification class
123 2093 tao
     *
124 2067 jones
     * @param queryspec
125
     *            the XML representation of the query (should conform to
126
     *            pathquery.dtd) as a Reader
127
     * @param parserName
128
     *            the fully qualified name of a Java Class implementing the
129
     *            org.xml.sax.XMLReader interface
130
     */
131
    public QuerySpecification(Reader queryspec, String parserName,
132
            String accNumberSeparator) throws IOException
133
    {
134
        super();
135
136
        // Initialize the class variables
137
        returnDocList = new Vector();
138
        filterDocList = new Vector();
139
        elementStack = new Stack();
140
        queryStack = new Stack();
141
        returnFieldList = new Vector();
142
        ownerList = new Vector();
143
        this.parserName = parserName;
144
        this.accNumberSeparator = accNumberSeparator;
145
146
        // Initialize the parser and read the queryspec
147
        XMLReader parser = initializeParser();
148
        if (parser == null) {
149
            System.err.println("SAX parser not instantiated properly.");
150
        }
151
        try {
152
            parser.parse(new InputSource(queryspec));
153
        } catch (SAXException e) {
154
            System.err.println("error parsing data in "
155
                    + "QuerySpecification.QuerySpecification");
156
            System.err.println(e.getMessage());
157
        }
158 181 jones
    }
159 2067 jones
160
    /**
161
     * construct an instance of the QuerySpecification class
162 2093 tao
     *
163 2067 jones
     * @param queryspec
164
     *            the XML representation of the query (should conform to
165
     *            pathquery.dtd) as a String
166
     * @param parserName
167
     *            the fully qualified name of a Java Class implementing the
168
     *            org.xml.sax.Parser interface
169
     */
170
    public QuerySpecification(String queryspec, String parserName,
171
            String accNumberSeparator) throws IOException
172
    {
173
        this(new StringReader(queryspec), parserName, accNumberSeparator);
174 155 jones
    }
175
176 2067 jones
    /**
177
     * construct an instance of the QuerySpecification class which don't need
178
     * to parser a xml document
179 2093 tao
     *
180 2067 jones
     * @param accNumberSeparator
181
     *            the separator between doc version
182
     */
183
    public QuerySpecification(String accNumberSeparator) throws IOException
184 2045 tao
    {
185 2067 jones
        // Initialize the class variables
186
        returnDocList = new Vector();
187
        filterDocList = new Vector();
188
        elementStack = new Stack();
189
        queryStack = new Stack();
190
        returnFieldList = new Vector();
191
        ownerList = new Vector();
192
        this.accNumberSeparator = accNumberSeparator;
193 2045 tao
    }
194 2067 jones
195
    /**
196
     * Method to set user name
197 2093 tao
     *
198 2067 jones
     * @param myName
199
     *            the user name
200
     */
201
    public void setUserName(String myName)
202 2045 tao
    {
203 2067 jones
        //to lower case
204
        if (myName != null) {
205
            this.userName = myName.toLowerCase();
206
        } else {
207
            this.userName = myName;
208
        }
209 2045 tao
    }
210 2067 jones
211
    /**
212
     * Method to set user group
213 2093 tao
     *
214 2067 jones
     * @param myGroup
215
     *            the user group
216
     */
217
    public void setGroup(String[] myGroup)
218 1301 tao
    {
219 2067 jones
        this.group = myGroup;
220 1301 tao
    }
221 2067 jones
222
    /**
223
     * Method to indicate this query is a percentage search
224
     */
225
    public boolean isPercentageSearch()
226 1301 tao
    {
227 2067 jones
        return percentageSearch;
228 1301 tao
    }
229 2067 jones
230
    /*
231
     * Method to get owner query. If it is owner it has all permission
232
     */
233
    private String createOwerQuery()
234 1301 tao
    {
235 2067 jones
        String ownerQuery = null;
236
        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
237
        if (userName != null && !userName.equals("")) {
238
            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
239
        }
240 155 jones
241 2663 sgarg
        logMetacat.info("OwnerQuery: " + ownerQuery);
242 2067 jones
        return ownerQuery;
243
    }
244
245
    /*
246
     * Method to create query for xml_access, this part is to get docid list
247
     * which have a allow rule for a given user
248
     */
249
    private String createAllowRuleQuery()
250 1301 tao
    {
251 2067 jones
        String allowQuery = null;
252
        String allowString = constructAllowString();
253
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
254
        allowQuery = allowQuery + ") AND subtreeid IS NULL";
255 2663 sgarg
        logMetacat.info("allow query is: " + allowQuery);
256 2067 jones
        return allowQuery;
257
258 1301 tao
    }
259 2067 jones
260
    /* Method to construct a allow rule string */
261
    private String constructAllowString()
262 1301 tao
    {
263 2067 jones
        String allowQuery = "";
264
        // add allow rule for user name
265
        if (userName != null && !userName.equals("")) {
266
            allowQuery = allowQuery + "(lower(principal_name) = '" + userName
267
                    + "' AND perm_type = 'allow'"
268
                    + " AND (permission='4' OR permission='7'))";
269
        }
270
        // add allow rule for public
271
        allowQuery = allowQuery + "OR (lower(principal_name) = '" + PUBLIC
272
                + "' AND perm_type = 'allow'"
273
                + " AND (permission='4' OR permission='7'))";
274
275
        // add allow rule for group
276
        if (group != null) {
277
            for (int i = 0; i < group.length; i++) {
278
                String groupUint = group[i];
279
                if (groupUint != null && !groupUint.equals("")) {
280
                    groupUint = groupUint.toLowerCase();
281
                    allowQuery = allowQuery + " OR (lower(principal_name) = '"
282
                            + groupUint + "' AND perm_type = 'allow'"
283
                            + " AND (permission='4' OR permission='7'))";
284
                }//if
285
            }//for
286 1301 tao
        }//if
287 2663 sgarg
        logMetacat.info("allow string is: " + allowQuery);
288 2067 jones
        return allowQuery;
289
    }
290 155 jones
291 2067 jones
    /*
292
     * Method to create query for xml_access, this part is to get docid list
293
     * which have a deny rule and perm_order is allowFirst for a given user.
294
     * This means the user will be denied to read
295
     */
296
    private String createDenyRuleQuery()
297
    {
298
        String denyQuery = null;
299
        String denyString = constructDenyString();
300
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
301
        denyQuery = denyQuery + ") AND subtreeid IS NULL ";
302 2663 sgarg
        logMetacat.info("denyquery is: " + denyQuery);
303 2067 jones
        return denyQuery;
304 711 jones
305 2067 jones
    }
306 181 jones
307 2067 jones
    /* Construct deny string */
308
    private String constructDenyString()
309 402 berkley
    {
310 2067 jones
        String denyQuery = "";
311
        // add deny rule for user name
312
        if (userName != null && !userName.equals("")) {
313
            denyQuery = denyQuery + "(lower(principal_name) = '" + userName
314
                    + "' AND perm_type = 'deny' "
315
                    + "AND perm_order ='allowFirst'"
316
                    + " AND (permission='4' OR permission='7'))";
317
        }
318
        // add deny rule for public
319
        denyQuery = denyQuery + "OR (lower(principal_name) = '" + PUBLIC
320
                + "' AND perm_type = 'deny' " + "AND perm_order ='allowFirst'"
321
                + " AND (permission='4' OR permission='7'))";
322
323
        // add allow rule for group
324
        if (group != null) {
325
            for (int i = 0; i < group.length; i++) {
326
                String groupUint = group[i];
327
                if (groupUint != null && !groupUint.equals("")) {
328
                    groupUint = groupUint.toLowerCase();
329
                    denyQuery = denyQuery + " OR (lower(principal_name) = '"
330
                            + groupUint + "' AND perm_type = 'deny' "
331
                            + "AND perm_order ='allowFirst'"
332
                            + " AND (permission='4' OR permission='7'))";
333
                }//if
334
            }//for
335
        }//if
336
        return denyQuery;
337 402 berkley
    }
338 2067 jones
339
    /**
340
     * Method to append a access control query to SQL. So in DBQuery class, we
341
     * can get docid from both user specified query and access control query.
342
     * We don't need to checking permission after we get the doclist. It will
343
     * be good to performance
344 2093 tao
     *
345 2067 jones
     */
346
    public String getAccessQuery()
347 402 berkley
    {
348 2067 jones
        String accessQuery = null;
349
        String onwer = createOwerQuery();
350
        String allow = createAllowRuleQuery();
351
        String deny = createDenyRuleQuery();
352
        accessQuery = " AND (docid IN(" + onwer + ")";
353
        accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
354
                + " AND docid NOT IN (" + deny + ")))";
355 2663 sgarg
        logMetacat.warn("accessquery is: " + accessQuery);
356 2067 jones
        return accessQuery;
357 402 berkley
    }
358 745 jones
359 2067 jones
    /**
360
     * Returns true if the parsed query contains and extended xml query (i.e.
361
     * there is at least one &lt;returnfield&gt; in the pathquery document)
362
     */
363
    public boolean containsExtendedSQL()
364
    {
365
        if (containsExtendedSQL) {
366
            return true;
367
        } else {
368
            return false;
369
        }
370
    }
371 745 jones
372 2067 jones
    /**
373
     * A method to get if the query has an attribute return field
374
     */
375 2472 cjones
    public boolean containsAttributeReturnField()
376 2067 jones
    {
377
        return hasAttributeReturnField;
378
    }
379 745 jones
380 2067 jones
    /**
381
     * Accessor method to return the identifier of this Query
382
     */
383
    public String getIdentifier()
384
    {
385
        return meta_file_id;
386
    }
387 155 jones
388 2067 jones
    /**
389
     * method to set the identifier of this query
390
     */
391
    public void setIdentifier(String id)
392
    {
393
        this.meta_file_id = id;
394
    }
395 745 jones
396 2067 jones
    /**
397
     * Accessor method to return the title of this Query
398
     */
399
    public String getQueryTitle()
400
    {
401
        return queryTitle;
402
    }
403 745 jones
404 2067 jones
    /**
405
     * method to set the title of this query
406
     */
407
    public void setQueryTitle(String title)
408
    {
409
        this.queryTitle = title;
410
    }
411 745 jones
412 2067 jones
    /**
413
     * Accessor method to return a vector of the return document types as
414
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
415
     */
416
    public Vector getReturnDocList()
417
    {
418
        return this.returnDocList;
419
    }
420 745 jones
421 2067 jones
    /**
422
     * method to set the list of return docs of this query
423
     */
424
    public void setReturnDocList(Vector returnDocList)
425
    {
426
        this.returnDocList = returnDocList;
427
    }
428 745 jones
429 2067 jones
    /**
430
     * Accessor method to return a vector of the filter doc types as defined in
431
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
432
     */
433
    public Vector getFilterDocList()
434
    {
435
        return this.filterDocList;
436
    }
437 172 jones
438 2067 jones
    /**
439
     * method to set the list of filter docs of this query
440
     */
441
    public void setFilterDocList(Vector filterDocList)
442
    {
443
        this.filterDocList = filterDocList;
444
    }
445 155 jones
446 2067 jones
    /**
447
     * Accessor method to return a vector of the extended return fields as
448
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
449
     */
450
    public Vector getReturnFieldList()
451
    {
452
        return this.returnFieldList;
453
    }
454 155 jones
455 2067 jones
    /**
456
     * method to set the list of fields to be returned by this query
457
     */
458
    public void setReturnFieldList(Vector returnFieldList)
459
    {
460
        this.returnFieldList = returnFieldList;
461
    }
462 155 jones
463 2067 jones
    /**
464
     * Accessor method to return a vector of the owner fields as defined in the
465
     * &lt;owner&gt; tag in the pathquery dtd.
466
     */
467
    public Vector getOwnerList()
468
    {
469
        return this.ownerList;
470
    }
471 155 jones
472 2067 jones
    /**
473
     * method to set the list of owners used to constrain this query
474
     */
475
    public void setOwnerList(Vector ownerList)
476
    {
477
        this.ownerList = ownerList;
478 155 jones
    }
479
480 2067 jones
    /**
481
     * get the QueryGroup used to express query constraints
482
     */
483
    public QueryGroup getQueryGroup()
484
    {
485
        return query;
486 158 jones
    }
487 155 jones
488 2067 jones
    /**
489
     * set the querygroup
490
     */
491
    public void setQueryGroup(QueryGroup group)
492
    {
493
        query = group;
494 158 jones
    }
495
496 2067 jones
    /**
497
     * set if this query sepcification has extendQuery(has return doc type or
498
     * not)
499
     */
500
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
501
    {
502
        containsExtendedSQL = hasExtenedQuery;
503
    }
504 158 jones
505 2067 jones
    /**
506
     * Set up the SAX parser for reading the XML serialized query
507
     */
508
    private XMLReader initializeParser()
509
    {
510
        XMLReader parser = null;
511
512
        // Set up the SAX document handlers for parsing
513
        try {
514
515
            // Get an instance of the parser
516
            parser = XMLReaderFactory.createXMLReader(parserName);
517
518
            // Set the ContentHandler to this instance
519
            parser.setContentHandler(this);
520
521
            // Set the error Handler to this instance
522
            parser.setErrorHandler(this);
523
524
        } catch (Exception e) {
525
            System.err.println("Error in QuerySpcecification.initializeParser "
526
                    + e.toString());
527
        }
528
529
        return parser;
530 1833 tao
    }
531 170 jones
532 2067 jones
    /**
533
     * callback method used by the SAX Parser when the start tag of an element
534
     * is detected. Used in this context to parse and store the query
535
     * information in class variables.
536
     */
537
    public void startElement(String uri, String localName, String qName,
538
            Attributes atts) throws SAXException
539
    {
540
        BasicNode currentNode = new BasicNode(localName);
541
        // add attributes to BasicNode here
542
        if (atts != null) {
543
            int len = atts.getLength();
544
            for (int i = 0; i < len; i++) {
545
                currentNode
546
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
547
            }
548
        }
549 170 jones
550 2067 jones
        elementStack.push(currentNode);
551
        if (currentNode.getTagName().equals("querygroup")) {
552
            QueryGroup currentGroup = new QueryGroup(currentNode
553
                    .getAttribute("operator"));
554
            if (query == null) {
555
                query = currentGroup;
556
            } else {
557
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
558
                parentGroup.addChild(currentGroup);
559
            }
560
            queryStack.push(currentGroup);
561
        }
562
    }
563 172 jones
564 2067 jones
    /**
565
     * callback method used by the SAX Parser when the end tag of an element is
566
     * detected. Used in this context to parse and store the query information
567
     * in class variables.
568
     */
569
    public void endElement(String uri, String localName, String qName)
570
            throws SAXException
571
    {
572
        BasicNode leaving = (BasicNode) elementStack.pop();
573
        if (leaving.getTagName().equals("queryterm")) {
574
            boolean isCaseSensitive = (new Boolean(leaving
575
                    .getAttribute("casesensitive"))).booleanValue();
576
            QueryTerm currentTerm = null;
577
            if (currentPathexpr == null) {
578
                currentTerm = new QueryTerm(isCaseSensitive, leaving
579
                        .getAttribute("searchmode"), currentValue);
580
            } else {
581
                currentTerm = new QueryTerm(isCaseSensitive, leaving
582
                        .getAttribute("searchmode"), currentValue,
583
                        currentPathexpr);
584
            }
585
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
586
            currentGroup.addChild(currentTerm);
587
            currentValue = null;
588
            currentPathexpr = null;
589
        } else if (leaving.getTagName().equals("querygroup")) {
590
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
591 2093 tao
        } else if (leaving.getTagName().equals("meta_file_id")) {
592
              meta_file_id = textBuffer.toString().trim();
593
        } else if (leaving.getTagName().equals("querytitle")) {
594
              queryTitle = textBuffer.toString().trim();
595
        } else if (leaving.getTagName().equals("value")) {
596
              currentValue = textBuffer.toString().trim();
597
        } else if (leaving.getTagName().equals("pathexpr")) {
598
              currentPathexpr = textBuffer.toString().trim();
599
        } else if (leaving.getTagName().equals("returndoctype")) {
600
              returnDocList.add(textBuffer.toString().trim());
601
        } else if (leaving.getTagName().equals("filterdoctype")) {
602
              filterDocList.add(textBuffer.toString().trim());
603
        } else if (leaving.getTagName().equals("returnfield")) {
604
              handleReturnField(textBuffer.toString().trim());
605
        } else if (leaving.getTagName().equals("filterdoctype")) {
606
              filterDocList.add(textBuffer.toString().trim());
607
        } else if (leaving.getTagName().equals("owner")) {
608
              ownerList.add(textBuffer.toString().trim());
609 172 jones
        }
610 2093 tao
611
        //rest textBuffer
612
        textBuffer = null;
613
        textBuffer = new StringBuffer();
614
615 172 jones
    }
616 743 jones
617 2067 jones
    /**
618
     * callback method used by the SAX Parser when the text sequences of an xml
619
     * stream are detected. Used in this context to parse and store the query
620
     * information in class variables.
621
     */
622
    public void characters(char ch[], int start, int length)
623
    {
624 2093 tao
      // buffer all text nodes for same element. This is for text was splited
625
      // into different nodes
626
      textBuffer.append(new String(ch, start, length));
627 2067 jones
628
    }
629
630
    /**
631
     * Method to transfer string to return field
632
     */
633
    public void handleReturnField(String inputString)
634
    {
635
        // make sure if return fields has an attribute or not
636
        if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
637
            // no attribute value will be returned
638 2663 sgarg
            logMetacat.info("QuerySpecification.handleReturnField(): " );
639
            logMetacat.info("  there are no attributes in the XPATH statement" );
640 2067 jones
            returnFieldList.add(inputString);
641
            containsExtendedSQL = true;
642 535 jones
        } else {
643 2474 sgarg
644 2467 cjones
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
645 2474 sgarg
646 2467 cjones
            // case where the return field is solely an attribute
647 2663 sgarg
            logMetacat.info("QuerySpecification.handleReturnField(): " );
648
            logMetacat.info("  there are *only* attributes in the XPATH statement" );
649 2472 cjones
            String returnPath = newPathExpressionWithOutAttribute(inputString);
650
            String attributeName = getAttributeName(inputString);
651
            Vector pathInfo = new Vector();
652
            // the vector has the information about return path and
653
            // attributename
654
            pathInfo.addElement(returnPath);
655
            pathInfo.addElement(attributeName);
656
            // put the vector into a hash table. The reseaon why don't put
657
            // return path or attributename as a key is because they are not
658
            // unique
659
            attributeReturnList.put(new Integer(countAttributeReturnField),
660
                    pathInfo);
661
            countAttributeReturnField++;
662
            hasAttributeReturnField = true;
663 2467 cjones
            containsExtendedSQL = true;
664
          } else {
665 2067 jones
            // has a attribute return field
666
            // divied the return filed into two parts, one is path and the
667
            // other is attribue name
668 2663 sgarg
            logMetacat.info("QuerySpecification.handleReturnField: " );
669
            logMetacat.info("  there are both attributes and elements" );
670
            logMetacat.info("  in the XPATH statement" );
671 2067 jones
            String returnPath = newPathExpressionWithOutAttribute(inputString);
672
            String attributeName = getAttributeName(inputString);
673
            Vector pathInfo = new Vector();
674
            // the vector has the information about return path and
675
            // attributename
676
            pathInfo.addElement(returnPath);
677
            pathInfo.addElement(attributeName);
678
            // put the vector into a hash table. The reseaon why don't put
679
            // return path or attributename as a key is because they are not
680
            // unique
681
            attributeReturnList.put(new Integer(countAttributeReturnField),
682
                    pathInfo);
683
            countAttributeReturnField++;
684
            hasAttributeReturnField = true;
685
            containsExtendedSQL = true;
686 2467 cjones
          }
687 535 jones
        }
688
    }
689
690 2067 jones
    /**
691
     * create a SQL serialization of the query that this instance represents
692
     */
693
    public String printSQL(boolean useXMLIndex)
694
    {
695
696
        StringBuffer self = new StringBuffer();
697 2366 sgarg
        StringBuffer queryString = new StringBuffer();
698 2067 jones
699 2366 sgarg
        queryString.append("SELECT docid,docname,doctype,");
700
        queryString.append("date_created, date_updated, rev ");
701
        queryString.append("FROM xml_documents WHERE");
702 2067 jones
703 2366 sgarg
        // Get the query from the QueryGroup and check
704
        // if no query has been returned
705
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
706 2677 sgarg
        logMetacat.info("Query from query in QuerySpec.printSQL: "
707
        		+ queryFromQueryGroup);
708
709 2373 sgarg
        if(!queryFromQueryGroup.trim().equals("")){
710 2366 sgarg
            self.append(" docid IN (");
711 2373 sgarg
            self.append(queryFromQueryGroup);
712 2366 sgarg
            self.append(") ");
713
        }
714 2067 jones
715
        // Add SQL to filter for doctypes requested in the query
716
        // This is an implicit OR for the list of doctypes. Only doctypes in
717
        // this
718
        // list will be searched if the tag is present
719
        if (!filterDocList.isEmpty()) {
720
            boolean firstdoctype = true;
721 2366 sgarg
            boolean emptyString = true;
722
723
            if(!self.toString().equals("")){
724
                self.append(" AND (");
725
                emptyString = false;
726
            }
727
728 2067 jones
            Enumeration en = filterDocList.elements();
729
            while (en.hasMoreElements()) {
730
                String currentDoctype = (String) en.nextElement();
731
                if (firstdoctype) {
732
                    firstdoctype = false;
733
                    self.append(" doctype = '" + currentDoctype + "'");
734
                } else {
735
                    self.append(" OR doctype = '" + currentDoctype + "'");
736
                }
737
            }
738 2366 sgarg
739
            if(!emptyString){
740
                self.append(") ");
741
            }
742 535 jones
        }
743 2067 jones
744
        // Add SQL to filter for owners requested in the query
745
        // This is an implicit OR for the list of owners
746
        if (!ownerList.isEmpty()) {
747
            boolean first = true;
748 2366 sgarg
            boolean emptyString = true;
749
750
            if(!self.toString().equals("")){
751
                self.append(" AND (");
752
                emptyString = false;
753
            }
754
755 2067 jones
            Enumeration en = ownerList.elements();
756
            while (en.hasMoreElements()) {
757
                String current = (String) en.nextElement();
758
                if (current != null) {
759
                    current = current.toLowerCase();
760
                }
761
                if (first) {
762
                    first = false;
763
                    self.append(" lower(user_owner) = '" + current + "'");
764
                } else {
765
                    self.append(" OR lower(user_owner) = '" + current + "'");
766
                }
767
            }
768 2366 sgarg
769
            if(!emptyString){
770
                self.append(") ");
771
            }
772 2067 jones
        }
773
774
        // if there is only one percentage search item, this query is a
775
        // percentage
776
        // search query
777 2663 sgarg
        logMetacat.info("percentage number: "
778
                + query.getPercentageSymbolCount());
779 2067 jones
        if (query.getPercentageSymbolCount() == 1) {
780 2663 sgarg
            logMetacat.info("It is a percentage search");
781 2067 jones
            percentageSearch = true;
782
        }
783
784 2366 sgarg
        queryString.append(self.toString());
785
        return queryString.toString();
786 535 jones
    }
787 2067 jones
788
    /**
789
     * This sql command will selecet startnodeid and endnodeid that user can
790
     * NOT access
791
     */
792
    public String printAccessControlSQLForReturnField(String doclist)
793 1350 tao
    {
794 2067 jones
        StringBuffer sql = new StringBuffer();
795
        String allowString = constructAllowString();
796
        String denyString = constructDenyString();
797
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
798
        sql.append("WHERE docid in (");
799
        sql.append(doclist);
800
        sql.append(") AND startnodeid IS NOT NULL AND ");
801
        sql.append("(");
802
        sql.append("(");
803 2068 jones
        sql
804
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
805 2067 jones
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
806
        sql.append(" AND lower(xml_documents.user_owner) ='");
807
        sql.append(userName);
808
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
809
        sql.append(")");
810
        sql.append(" AND ");
811
        sql.append("(");
812 2068 jones
        sql
813
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
814 2067 jones
        sql.append(allowString);
815
        sql.append(") AND (startnodeid IS NOT NULL))");
816
        sql.append(")");
817 2068 jones
        sql
818
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
819 2067 jones
        sql.append(denyString);
820
        sql.append(") AND (startnodeid IS NOT NULL))");
821
        sql.append(")");
822
        sql.append(")");
823
        sql.append(")");
824 2663 sgarg
        logMetacat.info("accessControlSQLForReturnField: "
825
                + sql.toString());
826 2067 jones
        return sql.toString();
827 1350 tao
    }
828 2067 jones
829
    /**
830
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
831 2069 jones
     * pathquery document. This allows for customization of the returned fields.
832 2093 tao
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
833
     * xml_nodes to find the fields to be included by their path expression, and
834 2069 jones
     * avoids the use of the xml_index table.
835 2093 tao
     *
836 2073 jones
     * @param doclist the list of document ids to search
837 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id) which
838 2073 jones
     *            this user should not access
839 2093 tao
     * @param useXMLIndex a boolean flag indicating whether to search using
840 2073 jones
     *            xml_index
841 2069 jones
     */
842
    public String printExtendedSQL(String doclist,
843
            Hashtable unaccessableNodePair, boolean useXMLIndex)
844
    {
845
        if (useXMLIndex) {
846
            return printExtendedSQL(doclist, unaccessableNodePair);
847
        } else {
848
            StringBuffer self = new StringBuffer();
849
850
            boolean firstfield = true;
851
            //put the returnfields into the query
852
            //the for loop allows for multiple fields
853
            for (int i = 0; i < returnFieldList.size(); i++) {
854
                if (firstfield) {
855
                    firstfield = false;
856
                } else {
857 2093 tao
                    self.append(" UNION ");
858 2069 jones
                }
859
                String path  = (String) returnFieldList.elementAt(i);
860
                self.append("select xml_nodes.docid, ");
861 2073 jones
                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
862 2069 jones
                self.append("xml_nodes.parentnodeid ");
863
                self.append("from xml_nodes, xml_documents ");
864
                self.append("where parentnodeid IN ");
865
                self.append(QueryTerm.useNestedStatements(path));
866 2093 tao
867 2069 jones
                self.append(" AND xml_nodes.docid in (");
868
                self.append(doclist);
869 2073 jones
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
870 2069 jones
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
871 2093 tao
872 2073 jones
                addAccessRestrictionSQL(unaccessableNodePair, self);
873 2069 jones
            }
874
875
            return self.toString();
876
        }
877
    }
878 2093 tao
879 2069 jones
    /**
880
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
881
     * pathquery document. This allows for customization of the returned fields.
882
     * It uses the xml_index table and so assumes that this table has been
883
     * built.
884 2093 tao
     *
885 2073 jones
     * @param doclist the list of document ids to search
886 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id)
887 2073 jones
     *            which this user should not access
888 2067 jones
     */
889 2093 tao
    public String printExtendedSQL(String doclist,
890 2067 jones
            Hashtable unaccessableNodePair)
891 402 berkley
    {
892 2663 sgarg
        logMetacat.info("querySpecification.printExtendedSQL called\n");
893 2067 jones
        StringBuffer self = new StringBuffer();
894 2472 cjones
895 2523 sgarg
        boolean usePathIndex = true;
896 2434 sgarg
897 2523 sgarg
        // test if the are elements in the return fields
898
        if ( returnFieldList.size() == 0 ) {
899
            return null;
900
        }
901 2067 jones
902 2523 sgarg
        for (int i = 0; i < returnFieldList.size(); i++) {
903
            if(!MetaCatUtil.pathsForIndexing.contains
904
               (returnFieldList.elementAt(i))){
905
                usePathIndex = false;
906
                break;
907
            }
908
        }
909 2073 jones
910 2523 sgarg
        if(usePathIndex){
911
            self.append("select docid, path, nodedata, parentnodeid ");
912
            self.append("from xml_path_index where (path like '");
913
914
            boolean firstfield = true;
915
            //put the returnfields into the query
916
            //the for loop allows for multiple fields
917
            for (int i = 0; i < returnFieldList.size(); i++) {
918
                if (firstfield) {
919
                    firstfield = false;
920
                    self.append( (String) returnFieldList.elementAt(i));
921
                    self.append("' ");
922
                }
923
                else {
924
                    self.append("or path like '");
925
                    self.append( (String) returnFieldList.elementAt(i));
926
                    self.append("' ");
927
                }
928
            }
929
            self.append(") AND docid in (");
930
            self.append(doclist);
931
            self.append(")");
932
933
        } else {
934
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
935
            self.append("xml_nodes.parentnodeid ");
936
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
937
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
938
939
            boolean firstfield = true;
940
            //put the returnfields into the query
941
            //the for loop allows for multiple fields
942
            for (int i = 0; i < returnFieldList.size(); i++) {
943
                if (firstfield) {
944
                    firstfield = false;
945
                    self.append((String) returnFieldList.elementAt(i));
946
                    self.append("' ");
947
                } else {
948
                    self.append("or xml_index.path like '");
949
                    self.append((String) returnFieldList.elementAt(i));
950
                    self.append("' ");
951
                }
952
            }
953
            self.append(") AND xml_nodes.docid in (");
954
            self.append(doclist);
955
            self.append(") AND xml_nodes.nodetype = 'TEXT'");
956
957
        }
958
959
        addAccessRestrictionSQL(unaccessableNodePair, self);
960
961
        return self.toString();
962 2073 jones
    }
963
964 2419 sgarg
965 2073 jones
    /**
966 2419 sgarg
     * Method to return a String generated after sorting the returnFieldList
967
     * Vector
968
     */
969
    public String getSortedReturnFieldString(){
970
        String returnFields = "";
971
972
        // Create a temporary vector and copy returnFieldList into it
973
        Vector tempVector = new Vector();
974 2464 sgarg
975 2419 sgarg
        Iterator it = returnFieldList.iterator();
976
        while(it.hasNext()){
977
            tempVector.add(it.next());
978
        }
979
980 2464 sgarg
        Enumeration attEnum = attributeReturnList.elements();
981
        while(attEnum.hasMoreElements()){
982
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
983
	    String rfield = "";
984
            if(tempIt.hasNext()){
985
		String element = (String)tempIt.next();
986 2474 sgarg
		if(element != null) {
987
		    rfield +=element;
988 2464 sgarg
		}
989
	    }
990
            if(tempIt.hasNext()){
991
		String attribute = (String)tempIt.next();
992 2474 sgarg
		if(attribute != null) {
993
  		    rfield = rfield + "@" + attribute;
994 2464 sgarg
                }
995
	    }
996
            tempVector.add(rfield);
997
        }
998
999 2419 sgarg
        // Sort the temporary vector
1000
        java.util.Collections.sort(tempVector);
1001
1002
        // Generate the string and return it
1003
        it = tempVector.iterator();
1004
        while(it.hasNext()){
1005
            returnFields = returnFields + it.next() + "|";
1006
        }
1007
        return returnFields;
1008
    }
1009
1010
1011
    /**
1012 2073 jones
     * Create the SQl necessary to restrict access to allowed nodes.  This is
1013
     * accomplished by restricting the nodes that are returned to include
1014
     * only those whose IDs fall outside of a set of start/stop pairs of
1015
     * nodeid values.  These pairs are passed in as a hash, with the key
1016
     * containing the start nodeid and the value containing the end nodeid.
1017
     * Any nodes between these start and end nodeid values will be excluded
1018
     * from the results.
1019 2093 tao
     *
1020 2073 jones
     * @param unaccessableNodePair hash of start/end nodeid pairs to restrict
1021 2093 tao
     * @param self a stringbuffer to which the genrated SQL is appended
1022 2073 jones
     */
1023 2093 tao
    private void addAccessRestrictionSQL(Hashtable unaccessableNodePair,
1024 2073 jones
            StringBuffer self)
1025
    {
1026 2067 jones
        // add control part for extended query
1027
        Enumeration en = unaccessableNodePair.keys();
1028
1029
        while (en.hasMoreElements()) {
1030
            // Get control pairs in object
1031
            Long startNodeIdObject = (Long) en.nextElement();
1032
            Long endNodeIdObject = (Long) unaccessableNodePair
1033
                    .get(startNodeIdObject);
1034
            // change it to long
1035
            long startNodeId = startNodeIdObject.longValue();
1036
            long endNodeId = endNodeIdObject.longValue();
1037
            // add into query
1038 2073 jones
            self.append(" AND ( xml_nodes.nodeid < ");
1039 2067 jones
            self.append(startNodeId);
1040
            self.append(" OR xml_nodes.nodeid > ");
1041
            self.append(endNodeId);
1042
            self.append(")");
1043
        }
1044 402 berkley
    }
1045 2074 jones
1046 2067 jones
    /**
1047 2074 jones
     * This method prints sql that finds the values of attributes in the xml
1048
     * documents based upon the whether the returnfield tag in the pathquery
1049 2093 tao
     * document has an attribute symbol (@). This allows for customization of
1050 2074 jones
     * the returned fields.
1051 2093 tao
     *
1052 2074 jones
     * @param doclist the list of document ids to search
1053 2093 tao
     * @param useXMLIndex a boolean flag indicating whether to search using
1054 2074 jones
     *            xml_index
1055 2067 jones
     */
1056 2074 jones
    public String printAttributeQuery(String doclist, boolean useXMLIndex)
1057
    {
1058
        if (useXMLIndex) {
1059
            return printAttributeQuery(doclist);
1060
        } else {
1061
            StringBuffer self = new StringBuffer();
1062
            boolean firstfield = true;
1063
            //put the returnfields attributes into the query
1064
            //the for loop allows for multiple fields and attributes
1065
            Enumeration returnAttributes = attributeReturnList.elements();
1066
            while (returnAttributes.hasMoreElements()) {
1067
                Vector currentVector = (Vector) returnAttributes.nextElement();
1068
                String returnPath = (String) currentVector.elementAt(0);
1069
                String attributeName = (String) currentVector.elementAt(1);
1070
                if (firstfield) {
1071
                    firstfield = false;
1072
                } else {
1073 2093 tao
                    self.append(" UNION ");
1074 2074 jones
                }
1075
                self.append("select xml_nodes.docid, '");
1076
                self.append(returnPath);
1077
                self.append("' as path, xml_nodes.nodedata, xml_nodes.nodename ");
1078
                self.append("from xml_nodes, xml_documents ");
1079
                self.append("where parentnodeid IN ");
1080
                self.append(QueryTerm.useNestedStatements(returnPath));
1081
                self.append(" AND xml_nodes.nodename like '");
1082
                self.append(attributeName);
1083
                self.append("' AND xml_nodes.docid in (");
1084
                self.append(doclist);
1085
                self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1086 2078 jones
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
1087 2074 jones
            }
1088 2093 tao
1089 2663 sgarg
            logMetacat.warn("Attribute query: " + self.toString());
1090 2074 jones
1091 2093 tao
            return self.toString();
1092 2074 jones
        }
1093
    }
1094
1095
    /**
1096
     * This method prints sql that finds the values of attributes in the xml
1097
     * documents based upon the whether the returnfield tag in the pathquery
1098 2093 tao
     * document has an attribute symbol (@). This allows for customization of
1099 2074 jones
     * the returned fields.
1100 2093 tao
     *
1101 2074 jones
     * @param doclist the list of document ids to search
1102
     */
1103 2067 jones
    public String printAttributeQuery(String doclist)
1104 1449 tao
    {
1105 2067 jones
        StringBuffer self = new StringBuffer();
1106
        self.append("select xml_nodes.docid, xml_index.path, ");
1107
        self.append("xml_nodes.nodedata, xml_nodes.nodename ");
1108
        self.append("from xml_index, xml_nodes where xml_index.nodeid=");
1109
        self.append("xml_nodes.parentnodeid and (");
1110
        boolean firstfield = true;
1111
        //put the returnfields attributes into the query
1112
        //the for loop allows for multiple fields and attributes
1113
        Enumeration returnAttributes = attributeReturnList.elements();
1114
        while (returnAttributes.hasMoreElements()) {
1115
            Vector currentVector = (Vector) returnAttributes.nextElement();
1116
            String returnPath = (String) currentVector.elementAt(0);
1117
            String attributeName = (String) currentVector.elementAt(1);
1118
            if (firstfield) {
1119
                firstfield = false;
1120 2466 sgarg
                self.append("( ");
1121
                if(returnPath != null){
1122
                    self.append("xml_index.path like '");
1123
                    self.append(returnPath);
1124
                    self.append("' AND ");
1125 2472 cjones
                }else {
1126 2663 sgarg
                  logMetacat.info("QuerySpecification.printAttributeQuery: "
1127
                   + "returnPath is: " + returnPath);
1128 2466 sgarg
                }
1129
                self.append("xml_nodes.nodename like '");
1130 2067 jones
                self.append(attributeName);
1131
                self.append("') ");
1132
            } else {
1133 2466 sgarg
                self.append(" or (");
1134
                if(returnPath != null){
1135
                    self.append("xml_index.path like '");
1136
                    self.append(returnPath);
1137
                    self.append("' AND ");
1138 2472 cjones
                }else {
1139 2663 sgarg
                  logMetacat.info("QuerySpecification.printAttributeQuery: "
1140
                   + "returnPath is null: " + returnPath);
1141 2466 sgarg
                }
1142
                self.append("xml_nodes.nodename like '");
1143 2067 jones
                self.append(attributeName);
1144
                self.append("') ");
1145
            }
1146
        }
1147
        self.append(") AND xml_nodes.docid in (");
1148
        self.append(doclist);
1149 2074 jones
        self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1150 2663 sgarg
        logMetacat.warn("Attribute query: " + self.toString());
1151 2067 jones
1152
        return self.toString();
1153 1449 tao
    }
1154 405 berkley
1155 2067 jones
    public static String printRelationSQL(String docid)
1156 1354 tao
    {
1157 2067 jones
        StringBuffer self = new StringBuffer();
1158
        self.append("select subject, relationship, object, subdoctype, ");
1159
        self.append("objdoctype from xml_relation ");
1160
        self.append("where docid like '").append(docid).append("'");
1161
        return self.toString();
1162 1354 tao
    }
1163 2066 jones
1164 2067 jones
    public static String printGetDocByDoctypeSQL(String docid)
1165
    {
1166
        StringBuffer self = new StringBuffer();
1167 465 berkley
1168 2067 jones
        self.append("SELECT docid,docname,doctype,");
1169
        self.append("date_created, date_updated ");
1170
        self.append("FROM xml_documents WHERE docid IN (");
1171
        self.append(docid).append(")");
1172
        return self.toString();
1173
    }
1174 159 jones
1175 2067 jones
    /**
1176
     * create a String description of the query that this instance represents.
1177
     * This should become a way to get the XML serialization of the query.
1178
     */
1179
    public String toString()
1180
    {
1181
        return "meta_file_id=" + meta_file_id + "\n" + query;
1182
        //DOCTITLE attr cleared from the db
1183
        //return "meta_file_id=" + meta_file_id + "\n" +
1184
        //"querytitle=" + querytitle + "\n" + query;
1185
    }
1186
1187 2073 jones
    /** A method to get rid of attribute part in path expression */
1188 2067 jones
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1189
    {
1190
        if (pathExpression == null) { return null; }
1191
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1192
        String newExpression = null;
1193 2458 cjones
        if (index != 0) {
1194 2067 jones
            newExpression = pathExpression.substring(0, index - 1);
1195
        }
1196 2663 sgarg
        logMetacat.info("The path expression without attributes: "
1197
                + newExpression);
1198 2067 jones
        return newExpression;
1199
    }
1200
1201 2073 jones
    /** A method to get attribute name from path */
1202 2067 jones
    public static String getAttributeName(String path)
1203
    {
1204
        if (path == null) { return null; }
1205
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1206
        int size = path.length();
1207
        String attributeName = null;
1208
        if (index != 1) {
1209
            attributeName = path.substring(index + 1, size);
1210
        }
1211 2663 sgarg
        logMetacat.info("The attirbute name from path: "
1212
                + attributeName);
1213 2067 jones
        return attributeName;
1214
    }
1215
1216 155 jones
}