1 |
1 |
/**
|
2 |
2 |
* Name: QuerySpecification.java
|
3 |
3 |
* Purpose: A Class that represents a structured query, and can be
|
4 |
|
* constructed from an XML serialization
|
|
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.
|
5 |
7 |
* Copyright: 2000 Regents of the University of California and the
|
6 |
8 |
* National Center for Ecological Analysis and Synthesis
|
7 |
9 |
* Authors: Matt Jones
|
... | ... | |
23 |
25 |
import org.xml.sax.SAXException;
|
24 |
26 |
import org.xml.sax.SAXParseException;
|
25 |
27 |
import org.xml.sax.helpers.ParserFactory;
|
26 |
|
//import oracle.xml.parser.v2.SAXParser;
|
27 |
28 |
|
28 |
|
|
29 |
|
|
30 |
29 |
/**
|
31 |
|
* A Class that represents a structured query,and can be constructed from an
|
32 |
|
* XML serialization conforming to "pathquery.dtd"
|
|
30 |
* A Class that represents a structured query, and can be
|
|
31 |
* constructed from an XML serialization conforming to @see pathquery.dtd.
|
|
32 |
* The printSQL() method can be used to print a SQL serialization of the query.
|
33 |
33 |
*/
|
34 |
34 |
public class QuerySpecification extends HandlerBase {
|
35 |
35 |
|
... | ... | |
41 |
41 |
// Query data structures
|
42 |
42 |
private String meta_file_id;
|
43 |
43 |
private String querytitle;
|
|
44 |
private Vector doctypeList;
|
44 |
45 |
private QueryGroup query = null;
|
45 |
46 |
|
46 |
47 |
private Stack elementStack;
|
47 |
48 |
private Stack queryStack;
|
48 |
49 |
private String currentValue;
|
49 |
50 |
private String currentPathexpr;
|
|
51 |
private String parserName = null;
|
50 |
52 |
|
51 |
53 |
/**
|
52 |
54 |
* construct an instance of the QuerySpecification class
|
53 |
55 |
*
|
54 |
|
* @param queryspec the XML representation of the query as a Reader
|
|
56 |
* @param queryspec the XML representation of the query (should conform
|
|
57 |
* to pathquery.dtd) as a Reader
|
|
58 |
* @param parserName the fully qualified name of a Java Class implementing
|
|
59 |
* the org.xml.sax.Parser interface
|
55 |
60 |
*/
|
56 |
|
public QuerySpecification( Reader queryspec ) throws IOException {
|
|
61 |
public QuerySpecification( Reader queryspec, String parserName )
|
|
62 |
throws IOException {
|
57 |
63 |
super();
|
58 |
64 |
|
59 |
|
// Initialize the stack
|
|
65 |
// Initialize the class variables
|
|
66 |
doctypeList = new Vector();
|
60 |
67 |
elementStack = new Stack();
|
61 |
68 |
queryStack = new Stack();
|
|
69 |
this.parserName = parserName;
|
62 |
70 |
|
63 |
71 |
// Initialize the parser and read the queryspec
|
64 |
72 |
Parser parser = initializeParser();
|
... | ... | |
73 |
81 |
/**
|
74 |
82 |
* construct an instance of the QuerySpecification class
|
75 |
83 |
*
|
76 |
|
* @param queryspec the XML representation of the query as a String
|
|
84 |
* @param queryspec the XML representation of the query (should conform
|
|
85 |
* to pathquery.dtd) as a String
|
|
86 |
* @param parserName the fully qualified name of a Java Class implementing
|
|
87 |
* the org.xml.sax.Parser interface
|
77 |
88 |
*/
|
78 |
|
public QuerySpecification( String queryspec ) throws IOException {
|
79 |
|
|
80 |
|
this(new StringReader(queryspec));
|
|
89 |
public QuerySpecification( String queryspec, String parserName )
|
|
90 |
throws IOException {
|
|
91 |
this(new StringReader(queryspec), parserName);
|
81 |
92 |
}
|
82 |
93 |
|
83 |
94 |
/** Main routine for testing */
|
... | ... | |
92 |
103 |
|
93 |
104 |
try {
|
94 |
105 |
FileReader xml = new FileReader(new File(xmlfile));
|
95 |
|
QuerySpecification qspec = new QuerySpecification(xml);
|
96 |
|
System.out.println(qspec.toSQL());
|
|
106 |
QuerySpecification qspec = new QuerySpecification(xml, DEFAULT_PARSER);
|
|
107 |
System.out.println(qspec.printSQL());
|
97 |
108 |
} catch (IOException e) {
|
98 |
109 |
System.err.println(e.getMessage());
|
99 |
110 |
}
|
... | ... | |
101 |
112 |
}
|
102 |
113 |
}
|
103 |
114 |
|
|
115 |
/**
|
|
116 |
* Set up the SAX parser for reading the XML serialized query
|
|
117 |
*/
|
104 |
118 |
private Parser initializeParser() {
|
105 |
119 |
Parser parser = null;
|
106 |
|
//
|
|
120 |
|
107 |
121 |
// Set up the SAX document handlers for parsing
|
108 |
|
//
|
109 |
122 |
try {
|
110 |
123 |
|
111 |
124 |
// Get an instance of the parser
|
112 |
|
parser = ParserFactory.makeParser(DEFAULT_PARSER);
|
|
125 |
parser = ParserFactory.makeParser(parserName);
|
113 |
126 |
|
114 |
127 |
// Set the DocumentHandler to this instance
|
115 |
128 |
parser.setDocumentHandler(this);
|
... | ... | |
124 |
137 |
return parser;
|
125 |
138 |
}
|
126 |
139 |
|
|
140 |
/**
|
|
141 |
* callback method used by the SAX Parser when the start tag of an
|
|
142 |
* element is detected. Used in this context to parse and store
|
|
143 |
* the query information in class variables.
|
|
144 |
*/
|
127 |
145 |
public void startElement (String name, AttributeList atts)
|
128 |
146 |
throws SAXException {
|
129 |
147 |
|
... | ... | |
150 |
168 |
}
|
151 |
169 |
}
|
152 |
170 |
|
|
171 |
/**
|
|
172 |
* callback method used by the SAX Parser when the end tag of an
|
|
173 |
* element is detected. Used in this context to parse and store
|
|
174 |
* the query information in class variables.
|
|
175 |
*/
|
153 |
176 |
public void endElement (String name) throws SAXException {
|
154 |
177 |
BasicNode leaving = (BasicNode)elementStack.pop();
|
155 |
178 |
if (leaving.getTagName().equals("queryterm")) {
|
... | ... | |
173 |
196 |
}
|
174 |
197 |
}
|
175 |
198 |
|
|
199 |
/**
|
|
200 |
* callback method used by the SAX Parser when the text sequences of an
|
|
201 |
* xml stream are detected. Used in this context to parse and store
|
|
202 |
* the query information in class variables.
|
|
203 |
*/
|
176 |
204 |
public void characters(char ch[], int start, int length) {
|
177 |
205 |
|
178 |
206 |
String inputString = new String(ch, start, length);
|
... | ... | |
186 |
214 |
currentValue = inputString;
|
187 |
215 |
} else if (currentTag.equals("pathexpr")) {
|
188 |
216 |
currentPathexpr = inputString;
|
|
217 |
} else if (currentTag.equals("returndoctype")) {
|
|
218 |
doctypeList.add(inputString);
|
189 |
219 |
}
|
190 |
220 |
}
|
191 |
221 |
|
192 |
|
public String toSQL() {
|
|
222 |
|
|
223 |
/**
|
|
224 |
* create a SQL serialization of the query that this instance represents
|
|
225 |
*/
|
|
226 |
public String printSQL() {
|
193 |
227 |
StringBuffer self = new StringBuffer();
|
194 |
228 |
|
|
229 |
// This determines the returned results
|
|
230 |
self.append("SELECT docid,docname,doctype,doctitle ");
|
|
231 |
self.append("FROM xml_documents WHERE docid IN (");
|
195 |
232 |
self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n");
|
196 |
|
self.append(query.toSQL());
|
197 |
233 |
|
|
234 |
// This determines the WHERE conditions
|
|
235 |
self.append(query.printSQL());
|
|
236 |
|
|
237 |
self.append(") ");
|
|
238 |
|
|
239 |
// Add SQL to filter for doctypes requested in the query
|
|
240 |
if (!doctypeList.isEmpty()) {
|
|
241 |
boolean firstdoctype = true;
|
|
242 |
self.append(" AND (");
|
|
243 |
Enumeration en = doctypeList.elements();
|
|
244 |
while (en.hasMoreElements()) {
|
|
245 |
String currentDoctype = (String)en.nextElement();
|
|
246 |
if (firstdoctype) {
|
|
247 |
firstdoctype = false;
|
|
248 |
self.append(" doctype = '" + currentDoctype + "'");
|
|
249 |
} else {
|
|
250 |
self.append(" OR doctype = '" + currentDoctype + "'");
|
|
251 |
}
|
|
252 |
}
|
|
253 |
self.append(") ");
|
|
254 |
}
|
|
255 |
|
198 |
256 |
return self.toString();
|
199 |
257 |
}
|
200 |
258 |
|
|
259 |
/**
|
|
260 |
* create a String description of the query that this instance represents.
|
|
261 |
* This should become a way to get the XML serialization of the query.
|
|
262 |
*/
|
201 |
263 |
public String toString() {
|
202 |
264 |
return "meta_file_id=" + meta_file_id + "\n" +
|
203 |
265 |
"querytitle=" + querytitle + "\n" + query;
|
... | ... | |
208 |
270 |
private String booleantype = null; // indicates how query terms are combined
|
209 |
271 |
private Vector children = null; // the list of query terms and groups
|
210 |
272 |
|
|
273 |
/**
|
|
274 |
* construct a new QueryGroup
|
|
275 |
*
|
|
276 |
* @param booleantype the boolean conector used to connect query terms
|
|
277 |
* in this query group
|
|
278 |
*/
|
211 |
279 |
public QueryGroup(String booleantype) {
|
212 |
280 |
this.booleantype = booleantype;
|
213 |
281 |
children = new Vector();
|
214 |
282 |
}
|
215 |
283 |
|
|
284 |
/**
|
|
285 |
* Add a child QueryGroup to this QueryGroup
|
|
286 |
*
|
|
287 |
* @param qgroup the query group to be added to the list of terms
|
|
288 |
*/
|
216 |
289 |
public void addChild(QueryGroup qgroup) {
|
217 |
290 |
children.add((Object)qgroup);
|
218 |
291 |
}
|
219 |
292 |
|
|
293 |
/**
|
|
294 |
* Add a child QueryTerm to this QueryGroup
|
|
295 |
*
|
|
296 |
* @param qterm the query term to be added to the list of terms
|
|
297 |
*/
|
220 |
298 |
public void addChild(QueryTerm qterm) {
|
221 |
299 |
children.add((Object)qterm);
|
222 |
300 |
}
|
223 |
301 |
|
|
302 |
/**
|
|
303 |
* Retrieve an Enumeration of query terms for this QueryGroup
|
|
304 |
*/
|
224 |
305 |
public Enumeration getChildren() {
|
225 |
306 |
return children.elements();
|
226 |
307 |
}
|
227 |
308 |
|
228 |
|
public String toSQL() {
|
|
309 |
/**
|
|
310 |
* create a SQL serialization of the query that this instance represents
|
|
311 |
*/
|
|
312 |
public String printSQL() {
|
229 |
313 |
StringBuffer self = new StringBuffer();
|
230 |
314 |
boolean first = true;
|
231 |
315 |
|
... | ... | |
241 |
325 |
}
|
242 |
326 |
if (qobject instanceof QueryGroup) {
|
243 |
327 |
QueryGroup qg = (QueryGroup)qobject;
|
244 |
|
self.append(qg.toSQL());
|
|
328 |
self.append(qg.printSQL());
|
245 |
329 |
} else if (qobject instanceof QueryTerm) {
|
246 |
330 |
QueryTerm qt = (QueryTerm)qobject;
|
247 |
|
self.append(qt.toSQL());
|
|
331 |
self.append(qt.printSQL());
|
248 |
332 |
} else {
|
249 |
333 |
System.err.println("qobject wrong type: fatal error");
|
250 |
334 |
}
|
... | ... | |
253 |
337 |
return self.toString();
|
254 |
338 |
}
|
255 |
339 |
|
|
340 |
/**
|
|
341 |
* create a String description of the query that this instance represents.
|
|
342 |
* This should become a way to get the XML serialization of the query.
|
|
343 |
*/
|
256 |
344 |
public String toString() {
|
257 |
345 |
StringBuffer self = new StringBuffer();
|
258 |
346 |
|
... | ... | |
274 |
362 |
private String value = null;
|
275 |
363 |
private String pathexpr = null;
|
276 |
364 |
|
|
365 |
/**
|
|
366 |
* Construct a new instance of a query term for a free text search
|
|
367 |
* (using the value only)
|
|
368 |
*
|
|
369 |
* @param casesensitive flag indicating whether case is used to match
|
|
370 |
* @param searchmode determines what kind of substring match is performed
|
|
371 |
* (one of starts-with|ends-with|contains|matches-exactly)
|
|
372 |
* @param value the text value to match
|
|
373 |
*/
|
277 |
374 |
public QueryTerm(boolean casesensitive, String searchmode,
|
278 |
375 |
String value) {
|
279 |
376 |
this.casesensitive = casesensitive;
|
... | ... | |
281 |
378 |
this.value = value;
|
282 |
379 |
}
|
283 |
380 |
|
|
381 |
/**
|
|
382 |
* Construct a new instance of a query term for a structured search
|
|
383 |
* (matching the value only for those nodes in the pathexpr)
|
|
384 |
*
|
|
385 |
* @param casesensitive flag indicating whether case is used to match
|
|
386 |
* @param searchmode determines what kind of substring match is performed
|
|
387 |
* (one of starts-with|ends-with|contains|matches-exactly)
|
|
388 |
* @param value the text value to match
|
|
389 |
* @param pathexpr the hierarchical path to the nodes to be searched
|
|
390 |
*/
|
284 |
391 |
public QueryTerm(boolean casesensitive, String searchmode,
|
285 |
392 |
String value, String pathexpr) {
|
286 |
393 |
this(casesensitive, searchmode, value);
|
287 |
394 |
this.pathexpr = pathexpr;
|
288 |
395 |
}
|
289 |
396 |
|
|
397 |
/** determine if the QueryTerm is case sensitive */
|
290 |
398 |
public boolean isCaseSensitive() {
|
291 |
399 |
return casesensitive;
|
292 |
400 |
}
|
293 |
401 |
|
|
402 |
/** get the searchmode parameter */
|
294 |
403 |
public String getSearchMode() {
|
295 |
404 |
return searchmode;
|
296 |
405 |
}
|
297 |
406 |
|
|
407 |
/** get the Value parameter */
|
298 |
408 |
public String getValue() {
|
299 |
409 |
return value;
|
300 |
410 |
}
|
301 |
411 |
|
|
412 |
/** get the path expression parameter */
|
302 |
413 |
public String getPathExpression() {
|
303 |
414 |
return pathexpr;
|
304 |
415 |
}
|
305 |
416 |
|
306 |
|
public String toSQL() {
|
|
417 |
/**
|
|
418 |
* create a SQL serialization of the query that this instance represents
|
|
419 |
*/
|
|
420 |
public String printSQL() {
|
307 |
421 |
StringBuffer self = new StringBuffer();
|
308 |
422 |
|
309 |
|
//self.append(" Query Term iscasesensitive=" + casesensitive + "\n");
|
310 |
|
|
311 |
423 |
// Uppercase the search string if case match is not important
|
312 |
424 |
String casevalue = null;
|
313 |
425 |
String nodedataterm = null;
|
... | ... | |
348 |
460 |
return self.toString();
|
349 |
461 |
}
|
350 |
462 |
|
|
463 |
/**
|
|
464 |
* create a String description of the query that this instance represents.
|
|
465 |
* This should become a way to get the XML serialization of the query.
|
|
466 |
*/
|
351 |
467 |
public String toString() {
|
352 |
468 |
StringBuffer self = new StringBuffer();
|
353 |
469 |
|
completed work on creating structured query prototype in DBQuery and QuerySpecification classes