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
 *    Release: @release@
11
 *
12
 *   '$Author: jones $'
13
 *     '$Date: 2004-03-30 16:05:21 -0800 (Tue, 30 Mar 2004) $'
14
 * '$Revision: 2078 $'
15
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 2 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
 */
30

    
31
package edu.ucsb.nceas.metacat;
32

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

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

    
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

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

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

    
63
    /** Identifier for this query document */
64
    private String meta_file_id;
65

    
66
    /** Title of this query */
67
    private String queryTitle;
68

    
69
    /** List of document types to be returned using package back tracing */
70
    private Vector returnDocList;
71

    
72
    /** List of document types to be searched */
73
    private Vector filterDocList;
74

    
75
    /** List of fields to be returned in result set */
76
    private Vector returnFieldList;
77

    
78
    /** List of users owning documents to be searched */
79
    private Vector ownerList;
80

    
81
    /** List of sites/scopes used to constrain search */
82
    private Vector siteList;
83

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

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

    
90
    private Stack queryStack;
91

    
92
    private String currentValue;
93

    
94
    private String currentPathexpr;
95

    
96
    private String parserName = null;
97

    
98
    private String accNumberSeparator = null;
99

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

    
102
    private boolean percentageSearch = false;
103

    
104
    private String userName = null;
105

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

    
108
    private String[] group = null;
109

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

    
112
    private boolean hasAttributeReturnField = false;
113

    
114
    private Hashtable attributeReturnList = new Hashtable();
115

    
116
    private int countAttributeReturnField = 0;
117

    
118
    /**
119
     * construct an instance of the QuerySpecification class
120
     * 
121
     * @param queryspec
122
     *            the XML representation of the query (should conform to
123
     *            pathquery.dtd) as a Reader
124
     * @param parserName
125
     *            the fully qualified name of a Java Class implementing the
126
     *            org.xml.sax.XMLReader interface
127
     */
128
    public QuerySpecification(Reader queryspec, String parserName,
129
            String accNumberSeparator) throws IOException
130
    {
131
        super();
132

    
133
        // Initialize the class variables
134
        returnDocList = new Vector();
135
        filterDocList = new Vector();
136
        elementStack = new Stack();
137
        queryStack = new Stack();
138
        returnFieldList = new Vector();
139
        ownerList = new Vector();
140
        siteList = new Vector();
141
        this.parserName = parserName;
142
        this.accNumberSeparator = accNumberSeparator;
143

    
144
        // Initialize the parser and read the queryspec
145
        XMLReader parser = initializeParser();
146
        if (parser == null) {
147
            System.err.println("SAX parser not instantiated properly.");
148
        }
149
        try {
150
            parser.parse(new InputSource(queryspec));
151
        } catch (SAXException e) {
152
            System.err.println("error parsing data in "
153
                    + "QuerySpecification.QuerySpecification");
154
            System.err.println(e.getMessage());
155
        }
156
    }
157

    
158
    /**
159
     * construct an instance of the QuerySpecification class
160
     * 
161
     * @param queryspec
162
     *            the XML representation of the query (should conform to
163
     *            pathquery.dtd) as a String
164
     * @param parserName
165
     *            the fully qualified name of a Java Class implementing the
166
     *            org.xml.sax.Parser interface
167
     */
168
    public QuerySpecification(String queryspec, String parserName,
169
            String accNumberSeparator) throws IOException
170
    {
171
        this(new StringReader(queryspec), parserName, accNumberSeparator);
172
    }
173

    
174
    /**
175
     * construct an instance of the QuerySpecification class which don't need
176
     * to parser a xml document
177
     * 
178
     * @param accNumberSeparator
179
     *            the separator between doc version
180
     */
181
    public QuerySpecification(String accNumberSeparator) throws IOException
182
    {
183
        // Initialize the class variables
184
        returnDocList = new Vector();
185
        filterDocList = new Vector();
186
        elementStack = new Stack();
187
        queryStack = new Stack();
188
        returnFieldList = new Vector();
189
        ownerList = new Vector();
190
        siteList = new Vector();
191
        this.accNumberSeparator = accNumberSeparator;
192
    }
193

    
194
    /**
195
     * Method to set user name
196
     * 
197
     * @param myName
198
     *            the user name
199
     */
200
    public void setUserName(String myName)
201
    {
202
        //to lower case
203
        if (myName != null) {
204
            this.userName = myName.toLowerCase();
205
        } else {
206
            this.userName = myName;
207
        }
208
    }
209

    
210
    /**
211
     * Method to set user group
212
     * 
213
     * @param myGroup
214
     *            the user group
215
     */
216
    public void setGroup(String[] myGroup)
217
    {
218
        this.group = myGroup;
219
    }
220

    
221
    /**
222
     * Method to indicate this query is a percentage search
223
     */
224
    public boolean isPercentageSearch()
225
    {
226
        return percentageSearch;
227
    }
228

    
229
    /*
230
     * Method to get owner query. If it is owner it has all permission
231
     */
232
    private String createOwerQuery()
233
    {
234
        String ownerQuery = null;
235
        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
236
        if (userName != null && !userName.equals("")) {
237
            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
238
        }
239

    
240
        MetaCatUtil.debugMessage("OwnerQuery: " + ownerQuery, 30);
241
        return ownerQuery;
242
    }
243

    
244
    /*
245
     * Method to create query for xml_access, this part is to get docid list
246
     * which have a allow rule for a given user
247
     */
248
    private String createAllowRuleQuery()
249
    {
250
        String allowQuery = null;
251
        String allowString = constructAllowString();
252
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
253
        allowQuery = allowQuery + ") AND subtreeid IS NULL";
254
        MetaCatUtil.debugMessage("allow query is: " + allowQuery, 30);
255
        return allowQuery;
256

    
257
    }
258

    
259
    /* Method to construct a allow rule string */
260
    private String constructAllowString()
261
    {
262
        String allowQuery = "";
263
        // add allow rule for user name
264
        if (userName != null && !userName.equals("")) {
265
            allowQuery = allowQuery + "(lower(principal_name) = '" + userName
266
                    + "' AND perm_type = 'allow'"
267
                    + " AND (permission='4' OR permission='7'))";
268
        }
269
        // add allow rule for public
270
        allowQuery = allowQuery + "OR (lower(principal_name) = '" + PUBLIC
271
                + "' AND perm_type = 'allow'"
272
                + " AND (permission='4' OR permission='7'))";
273

    
274
        // add allow rule for group
275
        if (group != null) {
276
            for (int i = 0; i < group.length; i++) {
277
                String groupUint = group[i];
278
                if (groupUint != null && !groupUint.equals("")) {
279
                    groupUint = groupUint.toLowerCase();
280
                    allowQuery = allowQuery + " OR (lower(principal_name) = '"
281
                            + groupUint + "' AND perm_type = 'allow'"
282
                            + " AND (permission='4' OR permission='7'))";
283
                }//if
284
            }//for
285
        }//if
286
        MetaCatUtil.debugMessage("allow string is: " + allowQuery, 40);
287
        return allowQuery;
288
    }
289

    
290
    /*
291
     * Method to create query for xml_access, this part is to get docid list
292
     * which have a deny rule and perm_order is allowFirst for a given user.
293
     * This means the user will be denied to read
294
     */
295
    private String createDenyRuleQuery()
296
    {
297
        String denyQuery = null;
298
        String denyString = constructDenyString();
299
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
300
        denyQuery = denyQuery + ") AND subtreeid IS NULL ";
301
        MetaCatUtil.debugMessage("denyquery is: " + denyQuery, 30);
302
        return denyQuery;
303

    
304
    }
305

    
306
    /* Construct deny string */
307
    private String constructDenyString()
308
    {
309
        String denyQuery = "";
310
        // add deny rule for user name
311
        if (userName != null && !userName.equals("")) {
312
            denyQuery = denyQuery + "(lower(principal_name) = '" + userName
313
                    + "' AND perm_type = 'deny' "
314
                    + "AND perm_order ='allowFirst'"
315
                    + " AND (permission='4' OR permission='7'))";
316
        }
317
        // add deny rule for public
318
        denyQuery = denyQuery + "OR (lower(principal_name) = '" + PUBLIC
319
                + "' AND perm_type = 'deny' " + "AND perm_order ='allowFirst'"
320
                + " AND (permission='4' OR permission='7'))";
321

    
322
        // add allow rule for group
323
        if (group != null) {
324
            for (int i = 0; i < group.length; i++) {
325
                String groupUint = group[i];
326
                if (groupUint != null && !groupUint.equals("")) {
327
                    groupUint = groupUint.toLowerCase();
328
                    denyQuery = denyQuery + " OR (lower(principal_name) = '"
329
                            + groupUint + "' AND perm_type = 'deny' "
330
                            + "AND perm_order ='allowFirst'"
331
                            + " AND (permission='4' OR permission='7'))";
332
                }//if
333
            }//for
334
        }//if
335
        return denyQuery;
336
    }
337

    
338
    /**
339
     * Method to append a access control query to SQL. So in DBQuery class, we
340
     * can get docid from both user specified query and access control query.
341
     * We don't need to checking permission after we get the doclist. It will
342
     * be good to performance
343
     *  
344
     */
345
    public String getAccessQuery()
346
    {
347
        String accessQuery = null;
348
        String onwer = createOwerQuery();
349
        String allow = createAllowRuleQuery();
350
        String deny = createDenyRuleQuery();
351
        accessQuery = " AND (docid IN(" + onwer + ")";
352
        accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
353
                + " AND docid NOT IN (" + deny + ")))";
354
        MetaCatUtil.debugMessage("accessquery is: " + accessQuery, 30);
355
        return accessQuery;
356
    }
357

    
358
    /**
359
     * Returns true if the parsed query contains and extended xml query (i.e.
360
     * there is at least one &lt;returnfield&gt; in the pathquery document)
361
     */
362
    public boolean containsExtendedSQL()
363
    {
364
        if (containsExtendedSQL) {
365
            return true;
366
        } else {
367
            return false;
368
        }
369
    }
370

    
371
    /**
372
     * A method to get if the query has an attribute return field
373
     */
374
    public boolean containAttributeReturnField()
375
    {
376
        return hasAttributeReturnField;
377
    }
378

    
379
    /**
380
     * Accessor method to return the identifier of this Query
381
     */
382
    public String getIdentifier()
383
    {
384
        return meta_file_id;
385
    }
386

    
387
    /**
388
     * method to set the identifier of this query
389
     */
390
    public void setIdentifier(String id)
391
    {
392
        this.meta_file_id = id;
393
    }
394

    
395
    /**
396
     * Accessor method to return the title of this Query
397
     */
398
    public String getQueryTitle()
399
    {
400
        return queryTitle;
401
    }
402

    
403
    /**
404
     * method to set the title of this query
405
     */
406
    public void setQueryTitle(String title)
407
    {
408
        this.queryTitle = title;
409
    }
410

    
411
    /**
412
     * Accessor method to return a vector of the return document types as
413
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
414
     */
415
    public Vector getReturnDocList()
416
    {
417
        return this.returnDocList;
418
    }
419

    
420
    /**
421
     * method to set the list of return docs of this query
422
     */
423
    public void setReturnDocList(Vector returnDocList)
424
    {
425
        this.returnDocList = returnDocList;
426
    }
427

    
428
    /**
429
     * Accessor method to return a vector of the filter doc types as defined in
430
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
431
     */
432
    public Vector getFilterDocList()
433
    {
434
        return this.filterDocList;
435
    }
436

    
437
    /**
438
     * method to set the list of filter docs of this query
439
     */
440
    public void setFilterDocList(Vector filterDocList)
441
    {
442
        this.filterDocList = filterDocList;
443
    }
444

    
445
    /**
446
     * Accessor method to return a vector of the extended return fields as
447
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
448
     */
449
    public Vector getReturnFieldList()
450
    {
451
        return this.returnFieldList;
452
    }
453

    
454
    /**
455
     * method to set the list of fields to be returned by this query
456
     */
457
    public void setReturnFieldList(Vector returnFieldList)
458
    {
459
        this.returnFieldList = returnFieldList;
460
    }
461

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

    
471
    /**
472
     * method to set the list of owners used to constrain this query
473
     */
474
    public void setOwnerList(Vector ownerList)
475
    {
476
        this.ownerList = ownerList;
477
    }
478

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

    
488
    /**
489
     * method to set the list of sites used to constrain this query
490
     */
491
    public void setSiteList(Vector siteList)
492
    {
493
        this.siteList = siteList;
494
    }
495

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

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

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

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

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

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

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

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

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

    
545
        return parser;
546
    }
547

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

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

    
580
    /**
581
     * callback method used by the SAX Parser when the end tag of an element is
582
     * detected. Used in this context to parse and store the query information
583
     * in class variables.
584
     */
585
    public void endElement(String uri, String localName, String qName)
586
            throws SAXException
587
    {
588
        BasicNode leaving = (BasicNode) elementStack.pop();
589
        if (leaving.getTagName().equals("queryterm")) {
590
            boolean isCaseSensitive = (new Boolean(leaving
591
                    .getAttribute("casesensitive"))).booleanValue();
592
            QueryTerm currentTerm = null;
593
            if (currentPathexpr == null) {
594
                currentTerm = new QueryTerm(isCaseSensitive, leaving
595
                        .getAttribute("searchmode"), currentValue);
596
            } else {
597
                currentTerm = new QueryTerm(isCaseSensitive, leaving
598
                        .getAttribute("searchmode"), currentValue,
599
                        currentPathexpr);
600
            }
601
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
602
            currentGroup.addChild(currentTerm);
603
            currentValue = null;
604
            currentPathexpr = null;
605
        } else if (leaving.getTagName().equals("querygroup")) {
606
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
607
        }
608
    }
609

    
610
    /**
611
     * callback method used by the SAX Parser when the text sequences of an xml
612
     * stream are detected. Used in this context to parse and store the query
613
     * information in class variables.
614
     */
615
    public void characters(char ch[], int start, int length)
616
    {
617

    
618
        String inputString = new String(ch, start, length);
619
        BasicNode currentNode = (BasicNode) elementStack.peek();
620
        String currentTag = currentNode.getTagName();
621
        if (currentTag.equals("meta_file_id")) {
622
            meta_file_id = inputString;
623
        } else if (currentTag.equals("querytitle")) {
624
            queryTitle = inputString;
625
        } else if (currentTag.equals("value")) {
626
            currentValue = inputString;
627
        } else if (currentTag.equals("pathexpr")) {
628
            currentPathexpr = inputString;
629
        } else if (currentTag.equals("returndoctype")) {
630
            returnDocList.add(inputString);
631
        } else if (currentTag.equals("filterdoctype")) {
632
            filterDocList.add(inputString);
633
        } else if (currentTag.equals("returnfield")) {
634
            handleReturnField(inputString);
635
        } else if (currentTag.equals("filterdoctype")) {
636
            filterDocList.add(inputString);
637
        } else if (currentTag.equals("owner")) {
638
            ownerList.add(inputString);
639
        } else if (currentTag.equals("site")) {
640
            siteList.add(inputString);
641
        }
642
    }
643

    
644
    /**
645
     * Method to transfer string to return field
646
     */
647
    public void handleReturnField(String inputString)
648
    {
649
        // make sure if return fields has an attribute or not
650
        if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
651
            // no attribute value will be returned
652
            returnFieldList.add(inputString);
653
            containsExtendedSQL = true;
654
        } else {
655
            // has a attribute return field
656
            // divied the return filed into two parts, one is path and the
657
            // other is attribue name
658
            String returnPath = newPathExpressionWithOutAttribute(inputString);
659
            String attributeName = getAttributeName(inputString);
660
            Vector pathInfo = new Vector();
661
            // the vector has the information about return path and
662
            // attributename
663
            pathInfo.addElement(returnPath);
664
            pathInfo.addElement(attributeName);
665
            // put the vector into a hash table. The reseaon why don't put
666
            // return path or attributename as a key is because they are not
667
            // unique
668
            attributeReturnList.put(new Integer(countAttributeReturnField),
669
                    pathInfo);
670
            countAttributeReturnField++;
671
            hasAttributeReturnField = true;
672
            containsExtendedSQL = true;
673
        }
674
    }
675

    
676
    /**
677
     * create a SQL serialization of the query that this instance represents
678
     */
679
    public String printSQL(boolean useXMLIndex)
680
    {
681

    
682
        StringBuffer self = new StringBuffer();
683

    
684
        self.append("SELECT docid,docname,doctype,");
685
        self.append("date_created, date_updated, rev ");
686
        self.append("FROM xml_documents WHERE docid IN (");
687

    
688
        // This determines the documents that meet the query conditions
689
        self.append(query.printSQL(useXMLIndex));
690

    
691
        self.append(") ");
692

    
693
        // Add SQL to filter for doctypes requested in the query
694
        // This is an implicit OR for the list of doctypes. Only doctypes in
695
        // this
696
        // list will be searched if the tag is present
697
        if (!filterDocList.isEmpty()) {
698
            boolean firstdoctype = true;
699
            self.append(" AND (");
700
            Enumeration en = filterDocList.elements();
701
            while (en.hasMoreElements()) {
702
                String currentDoctype = (String) en.nextElement();
703
                if (firstdoctype) {
704
                    firstdoctype = false;
705
                    self.append(" doctype = '" + currentDoctype + "'");
706
                } else {
707
                    self.append(" OR doctype = '" + currentDoctype + "'");
708
                }
709
            }
710
            self.append(") ");
711
        }
712

    
713
        // Add SQL to filter for owners requested in the query
714
        // This is an implicit OR for the list of owners
715
        if (!ownerList.isEmpty()) {
716
            boolean first = true;
717
            self.append(" AND (");
718
            Enumeration en = ownerList.elements();
719
            while (en.hasMoreElements()) {
720
                String current = (String) en.nextElement();
721
                if (current != null) {
722
                    current = current.toLowerCase();
723
                }
724
                if (first) {
725
                    first = false;
726
                    self.append(" lower(user_owner) = '" + current + "'");
727
                } else {
728
                    self.append(" OR lower(user_owner) = '" + current + "'");
729
                }
730
            }
731
            self.append(") ");
732
        }
733

    
734
        // Add SQL to filter for sites requested in the query
735
        // This is an implicit OR for the list of sites
736
        if (!siteList.isEmpty()) {
737
            boolean first = true;
738
            self.append(" AND (");
739
            Enumeration en = siteList.elements();
740
            while (en.hasMoreElements()) {
741
                String current = (String) en.nextElement();
742
                if (first) {
743
                    first = false;
744
                    self.append(" SUBSTR(docid, 1, INSTR(docid, '"
745
                            + accNumberSeparator + "')-1) = '" + current + "'");
746
                } else {
747
                    self.append(" OR SUBSTR(docid, 1, INSTR(docid, '"
748
                            + accNumberSeparator + "')-1) = '" + current + "'");
749
                }
750
            }
751
            self.append(") ");
752
        }
753

    
754
        // if there is only one percentage search item, this query is a
755
        // percentage
756
        // search query
757
        MetaCatUtil.debugMessage("percentage number: "
758
                + query.getPercentageSymbolCount(), 35);
759
        if (query.getPercentageSymbolCount() == 1) {
760
            MetaCatUtil.debugMessage("it is a percentage search", 30);
761
            percentageSearch = true;
762
        }
763

    
764
        return self.toString();
765
    }
766

    
767
    /**
768
     * This sql command will selecet startnodeid and endnodeid that user can
769
     * NOT access
770
     */
771
    public String printAccessControlSQLForReturnField(String doclist)
772
    {
773
        StringBuffer sql = new StringBuffer();
774
        String allowString = constructAllowString();
775
        String denyString = constructDenyString();
776
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
777
        sql.append("WHERE docid in (");
778
        sql.append(doclist);
779
        sql.append(") AND startnodeid IS NOT NULL AND ");
780
        sql.append("(");
781
        sql.append("(");
782
        sql
783
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
784
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
785
        sql.append(" AND lower(xml_documents.user_owner) ='");
786
        sql.append(userName);
787
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
788
        sql.append(")");
789
        sql.append(" AND ");
790
        sql.append("(");
791
        sql
792
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
793
        sql.append(allowString);
794
        sql.append(") AND (startnodeid IS NOT NULL))");
795
        sql.append(")");
796
        sql
797
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
798
        sql.append(denyString);
799
        sql.append(") AND (startnodeid IS NOT NULL))");
800
        sql.append(")");
801
        sql.append(")");
802
        sql.append(")");
803
        MetaCatUtil.debugMessage("accessControlSQLForReturnField: "
804
                + sql.toString(), 30);
805
        return sql.toString();
806
    }
807

    
808
    /**
809
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
810
     * pathquery document. This allows for customization of the returned fields.
811
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on 
812
     * xml_nodes to find the fields to be included by their path expression, and 
813
     * avoids the use of the xml_index table.
814
     * 
815
     * @param doclist the list of document ids to search
816
     * @param unaccessableNodePair the node pairs (start id and end id) which 
817
     *            this user should not access
818
     * @param useXMLIndex a boolean flag indicating whether to search using 
819
     *            xml_index
820
     */
821
    public String printExtendedSQL(String doclist,
822
            Hashtable unaccessableNodePair, boolean useXMLIndex)
823
    {
824
        if (useXMLIndex) {
825
            return printExtendedSQL(doclist, unaccessableNodePair);
826
        } else {
827
            StringBuffer self = new StringBuffer();
828

    
829
            boolean firstfield = true;
830
            //put the returnfields into the query
831
            //the for loop allows for multiple fields
832
            for (int i = 0; i < returnFieldList.size(); i++) {
833
                if (firstfield) {
834
                    firstfield = false;
835
                } else {
836
                    self.append(" UNION ");   
837
                }
838
                String path  = (String) returnFieldList.elementAt(i);
839
                self.append("select xml_nodes.docid, ");
840
                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
841
                self.append("xml_nodes.parentnodeid ");
842
                self.append("from xml_nodes, xml_documents ");
843
                self.append("where parentnodeid IN ");
844
                self.append(QueryTerm.useNestedStatements(path));
845
                
846
                self.append(" AND xml_nodes.docid in (");
847
                self.append(doclist);
848
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
849
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
850
                
851
                addAccessRestrictionSQL(unaccessableNodePair, self);
852
            }
853

    
854
            return self.toString();
855
        }
856
    }
857
    
858
    /**
859
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
860
     * pathquery document. This allows for customization of the returned fields.
861
     * It uses the xml_index table and so assumes that this table has been
862
     * built.
863
     * 
864
     * @param doclist the list of document ids to search
865
     * @param unaccessableNodePair the node pairs (start id and end id) 
866
     *            which this user should not access
867
     */
868
    public String printExtendedSQL(String doclist, 
869
            Hashtable unaccessableNodePair)
870
    {
871
        StringBuffer self = new StringBuffer();
872
        self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata, ");
873
        self.append("xml_nodes.parentnodeid ");
874
        self.append("from xml_index, xml_nodes where xml_index.nodeid=");
875
        self.append("xml_nodes.parentnodeid and (xml_index.path like '");
876
        boolean firstfield = true;
877
        //put the returnfields into the query
878
        //the for loop allows for multiple fields
879
        for (int i = 0; i < returnFieldList.size(); i++) {
880
            if (firstfield) {
881
                firstfield = false;
882
                self.append((String) returnFieldList.elementAt(i));
883
                self.append("' ");
884
            } else {
885
                self.append("or xml_index.path like '");
886
                self.append((String) returnFieldList.elementAt(i));
887
                self.append("' ");
888
            }
889
        }
890
        self.append(") AND xml_nodes.docid in (");
891
        self.append(doclist);
892
        self.append(") AND xml_nodes.nodetype = 'TEXT'");
893

    
894
        addAccessRestrictionSQL(unaccessableNodePair, self);
895

    
896
        return self.toString();
897
    }
898

    
899
    /**
900
     * Create the SQl necessary to restrict access to allowed nodes.  This is
901
     * accomplished by restricting the nodes that are returned to include
902
     * only those whose IDs fall outside of a set of start/stop pairs of
903
     * nodeid values.  These pairs are passed in as a hash, with the key
904
     * containing the start nodeid and the value containing the end nodeid.
905
     * Any nodes between these start and end nodeid values will be excluded
906
     * from the results.
907
     * 
908
     * @param unaccessableNodePair hash of start/end nodeid pairs to restrict
909
     * @param self a stringbuffer to which the genrated SQL is appended 
910
     */
911
    private void addAccessRestrictionSQL(Hashtable unaccessableNodePair, 
912
            StringBuffer self)
913
    {
914
        // add control part for extended query
915
        Enumeration en = unaccessableNodePair.keys();
916

    
917
        while (en.hasMoreElements()) {
918
            // Get control pairs in object
919
            Long startNodeIdObject = (Long) en.nextElement();
920
            Long endNodeIdObject = (Long) unaccessableNodePair
921
                    .get(startNodeIdObject);
922
            // change it to long
923
            long startNodeId = startNodeIdObject.longValue();
924
            long endNodeId = endNodeIdObject.longValue();
925
            // add into query
926
            self.append(" AND ( xml_nodes.nodeid < ");
927
            self.append(startNodeId);
928
            self.append(" OR xml_nodes.nodeid > ");
929
            self.append(endNodeId);
930
            self.append(")");
931
        }
932
    }
933

    
934
    /**
935
     * This method prints sql that finds the values of attributes in the xml
936
     * documents based upon the whether the returnfield tag in the pathquery
937
     * document has an attribute symbol (@). This allows for customization of 
938
     * the returned fields.
939
     * 
940
     * @param doclist the list of document ids to search
941
     * @param useXMLIndex a boolean flag indicating whether to search using 
942
     *            xml_index
943
     */
944
    public String printAttributeQuery(String doclist, boolean useXMLIndex)
945
    {
946
        if (useXMLIndex) {
947
            return printAttributeQuery(doclist);
948
        } else {
949
            StringBuffer self = new StringBuffer();
950
            boolean firstfield = true;
951
            //put the returnfields attributes into the query
952
            //the for loop allows for multiple fields and attributes
953
            Enumeration returnAttributes = attributeReturnList.elements();
954
            while (returnAttributes.hasMoreElements()) {
955
                Vector currentVector = (Vector) returnAttributes.nextElement();
956
                String returnPath = (String) currentVector.elementAt(0);
957
                String attributeName = (String) currentVector.elementAt(1);
958
                if (firstfield) {
959
                    firstfield = false;
960
                } else {
961
                    self.append(" UNION ");   
962
                }
963
                self.append("select xml_nodes.docid, '");
964
                self.append(returnPath);
965
                self.append("' as path, xml_nodes.nodedata, xml_nodes.nodename ");
966
                self.append("from xml_nodes, xml_documents ");
967
                self.append("where parentnodeid IN ");
968
                self.append(QueryTerm.useNestedStatements(returnPath));
969
                self.append(" AND xml_nodes.nodename like '");
970
                self.append(attributeName);
971
                self.append("' AND xml_nodes.docid in (");
972
                self.append(doclist);
973
                self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
974
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
975
            }
976
            
977
            MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
978

    
979
            return self.toString();            
980
        }
981
    }
982

    
983
    /**
984
     * This method prints sql that finds the values of attributes in the xml
985
     * documents based upon the whether the returnfield tag in the pathquery
986
     * document has an attribute symbol (@). This allows for customization of 
987
     * the returned fields.
988
     * 
989
     * @param doclist the list of document ids to search
990
     */
991
    public String printAttributeQuery(String doclist)
992
    {
993
        StringBuffer self = new StringBuffer();
994
        self.append("select xml_nodes.docid, xml_index.path, ");
995
        self.append("xml_nodes.nodedata, xml_nodes.nodename ");
996
        self.append("from xml_index, xml_nodes where xml_index.nodeid=");
997
        self.append("xml_nodes.parentnodeid and (");
998
        boolean firstfield = true;
999
        //put the returnfields attributes into the query
1000
        //the for loop allows for multiple fields and attributes
1001
        Enumeration returnAttributes = attributeReturnList.elements();
1002
        while (returnAttributes.hasMoreElements()) {
1003
            Vector currentVector = (Vector) returnAttributes.nextElement();
1004
            String returnPath = (String) currentVector.elementAt(0);
1005
            String attributeName = (String) currentVector.elementAt(1);
1006
            if (firstfield) {
1007
                firstfield = false;
1008
                self.append("( xml_index.path like '");
1009
                self.append(returnPath);
1010
                self.append("' AND xml_nodes.nodename like '");
1011
                self.append(attributeName);
1012
                self.append("') ");
1013
            } else {
1014
                self.append(" or ( xml_index.path like '");
1015
                self.append(returnPath);
1016
                self.append("' AND xml_nodes.nodename like '");
1017
                self.append(attributeName);
1018
                self.append("') ");
1019
            }
1020
        }
1021
        self.append(") AND xml_nodes.docid in (");
1022
        self.append(doclist);
1023
        self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1024
        MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
1025

    
1026
        return self.toString();
1027
    }
1028

    
1029
    public static String printRelationSQL(String docid)
1030
    {
1031
        StringBuffer self = new StringBuffer();
1032
        self.append("select subject, relationship, object, subdoctype, ");
1033
        self.append("objdoctype from xml_relation ");
1034
        self.append("where docid like '").append(docid).append("'");
1035
        return self.toString();
1036
    }
1037

    
1038
    public static String printGetDocByDoctypeSQL(String docid)
1039
    {
1040
        StringBuffer self = new StringBuffer();
1041

    
1042
        self.append("SELECT docid,docname,doctype,");
1043
        self.append("date_created, date_updated ");
1044
        self.append("FROM xml_documents WHERE docid IN (");
1045
        self.append(docid).append(")");
1046
        return self.toString();
1047
    }
1048

    
1049
    /**
1050
     * create a String description of the query that this instance represents.
1051
     * This should become a way to get the XML serialization of the query.
1052
     */
1053
    public String toString()
1054
    {
1055
        return "meta_file_id=" + meta_file_id + "\n" + query;
1056
        //DOCTITLE attr cleared from the db
1057
        //return "meta_file_id=" + meta_file_id + "\n" +
1058
        //"querytitle=" + querytitle + "\n" + query;
1059
    }
1060

    
1061
    /** A method to get rid of attribute part in path expression */
1062
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1063
    {
1064
        if (pathExpression == null) { return null; }
1065
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1066
        String newExpression = null;
1067
        if (index != 1) {
1068
            newExpression = pathExpression.substring(0, index - 1);
1069
        }
1070
        MetaCatUtil.debugMessage("The path expression without attributes: "
1071
                + newExpression, 30);
1072
        return newExpression;
1073
    }
1074

    
1075
    /** A method to get attribute name from path */
1076
    public static String getAttributeName(String path)
1077
    {
1078
        if (path == null) { return null; }
1079
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1080
        int size = path.length();
1081
        String attributeName = null;
1082
        if (index != 1) {
1083
            attributeName = path.substring(index + 1, size);
1084
        }
1085
        MetaCatUtil.debugMessage("The attirbute name from path: "
1086
                + attributeName, 30);
1087
        return attributeName;
1088
    }
1089

    
1090
}
(48-48/58)