001: /*
002: * CONFIDENTIAL AND PROPRIETARY SOURCE CODE OF
003: * NETSCAPE COMMUNICATIONS CORPORATION
004: *
005: * Copyright (c) 1996 Netscape Communications Corporation.
006: * All Rights Reserved.
007: * Use of this Source Code is subject to the terms of the applicable
008: * license agreement from Netscape Communications Corporation.
009: */
010:
011: package soif;
012:
013: import communications.ReadConnection;
014: import util.ReportError;
015: import java.io.*;
016:
017: /**
018: * Search encapsulation class.
019: */
020: public class Search {
021:
022: /** Processing status: completed successfully. */
023: public final static int COMPLETE = 0;
024:
025: /** Processing status: initial state uponb construction. */
026: public final static int PREPARED = 1;
027:
028: /** Processing status: search in progress. */
029: public final static int PROCESSING = 2;
030:
031: /** Processing status: completed abnormally. */
032: public final static int ABEND = 3;
033:
034: /** Processing status: empty instance. */
035: public final static int EMPTY = 4;
036:
037: /** Processing status. */
038: private int status;
039:
040: /** The fully formatted URL query string that will be sent to the server. */
041: private String query;
042:
043: /** The query qualification. */
044: private String scope;
045:
046: /** The RDM server URL. */
047: private String RDMServer;
048:
049: /** The comma delimited desired result attributes. */
050: private String viewAttributes;
051:
052: /**
053: * The comma delimited sort order w/ +ascending and -descending.
054: * <P>Eg, "-score,+title". Default value is "-score".
055: */
056: private String viewOrder;
057:
058: /** The maximum number of results requested. */
059: private int viewHits;
060:
061: /** The offset of the first hit to be returned. */
062: private int firstHit;
063:
064: /** The RDM request type. */
065: private String RDMType;
066:
067: /** The query language. */
068: private String ql;
069:
070: /** The csid. */
071: private String csid;
072:
073: /** The result set as a string, including the rdm header. */
074: private String result;
075:
076: /** The result set as a stream, including the rdm header. */
077: private boolean streamMode = false;
078: private DataInputStream resultStream;
079:
080: /** The result header. */
081: private RDMHeader rdmHeader;
082:
083: /** The result set as a SOIF list, including the rdm header. */
084: private SOIF soifResult;
085:
086: /** Automatically parse a SOIF string. */
087: private boolean doSOIFParse;
088:
089: /**
090: * Constructor.
091: */
092: public Search() {
093: this .scope = "";
094: this .viewAttributes = "";
095: this .viewOrder = "";
096: this .firstHit = 0;
097: this .viewHits = 0;
098: this .ql = "";
099: this .csid = null;
100: this .query = "";
101: this .status = EMPTY;
102: this .doSOIFParse = false;
103: this .RDMServer = "";
104: this .RDMType = "rd-request";
105: }
106:
107: /**
108: * Constructor.
109: * <p>Default values implicit in this constructor are:<br>
110: * - viewAttributes: null. Return all attributes<br>
111: * - viewOrder: null. Return server default (ie, sorted by relevance)<br>
112: * - firsthit: 1. Start hits at hit number 1<br>
113: * - viewhits: 10. Return 10 hits only<br>
114: * - query language: compass. Search documents<br>
115: * - csid: null. Use the default csid for the server<br>
116: *
117: * @param scope the query qualification
118: * @param RDMServer Compass server URL, eg, http://compass.mycompany.com:2222/
119: * @since 3.01C
120: */
121: public Search(String scope, String RDMServer) {
122: this (scope, "", "", 1, 10, "compass", null, RDMServer);
123: }
124:
125: /**
126: * Constructor.
127: * @param scope the query qualification
128: * @param viewAttributes comma delimited desired result attributes
129: * @param viewOrder comma delimited sort order w/ +ascend and -descend
130: * @param viewHits maximum number of results requested
131: * @param ql query language
132: * @param csid Compass Server ID (can be null for server's default csid)
133: * @param RDMServer Compass Server URL
134: */
135: public Search(String scope, String viewAttributes,
136: String viewOrder, int firstHit, int viewHits, String ql,
137: CSID csid, String RDMServer) {
138: this .scope = scope;
139: this .viewAttributes = viewAttributes;
140: this .viewOrder = viewOrder;
141: this .firstHit = firstHit;
142: this .viewHits = viewHits;
143: this .ql = ql;
144: // csid is optional, if not supplied server will assume the default
145: if (csid != null)
146: this .csid = csid.toString();
147: this .RDMServer = RDMServer;
148: this .RDMType = "rd-request";
149: this .status = PREPARED;
150: this .doSOIFParse = true;
151: }
152:
153: /**
154: * Constructor.
155: * @param scope the query qualification
156: * @param viewAttributes comma delimited desired result attributes
157: * @param viewOrder comma delimited sort order w/ +ascend and -descend
158: * @param ql query language
159: * @param csid Compass Server ID (can be null for server's default csid)
160: * @param RDMServer Compass server URL, eg, http://compass/
161: * @deprecated 3.01C, see {@link #Search()}
162: */
163: public Search(String scope, String viewAttributes,
164: String viewOrder, String ql, CSID csid, String RDMServer) {
165: this (scope, viewAttributes, viewOrder, 1, 10, ql, csid,
166: RDMServer);
167: }
168:
169: /**
170: * @deprecated 3.01C, see {@link #Search()}
171: * Constructor.
172: * @param scope the query qualification
173: * @param viewAttributes comma delimited desired result attributes
174: * @param viewOrder comma delimited sort order w/ +ascend and -descend
175: * @param ql query language
176: * @param csid Compass Server ID (can be null for server's default csid)
177: * @param RDMServer Compass server URL, eg, http://compass/
178: */
179: public Search(String scope, String viewAttributes,
180: String viewOrder, int viewHits, String ql, CSID csid,
181: String RDMServer) {
182: this (scope, viewAttributes, viewOrder, 1, viewHits, ql, csid,
183: RDMServer);
184: }
185:
186: /**
187: * Set whether SOIF parsing is to be done or not.
188: * This can only be set when the status is PREPARED.
189: * By default, SOIF parsing is true.
190: */
191: public void setSOIFParse(boolean b) {
192: if (status == PREPARED)
193: doSOIFParse = b;
194: }
195:
196: /**
197: * Sets whether streaming is enabled or disabled.
198: * By default, this is set to false.<br>
199: * When stream mode is enabled, the search results
200: * RDM header SOIF will still be parsed, so that
201: * result, hit and document counts are available, but
202: * the document hit data will not be parsed. Instead, use
203: * {@link #getResultStream} to access the document
204: * SOIF DataInputStream directly.
205: *
206: * @since 3.01C
207: */
208: public void setStreamMode(boolean m) {
209: streamMode = m;
210: }
211:
212: /**
213: * Get the starting hit offset.
214: * @see #setFirstHit
215: */
216: public int getFirstHit() {
217: return firstHit;
218: }
219:
220: /**
221: * Set the starting hit offset.
222: * @see #setFirstHit
223: */
224: public void setFirstHit(int firstHit) {
225: this .firstHit = firstHit;
226: }
227:
228: /**
229: * Get the maximum number of hits returned.
230: * @see #setViewHits
231: */
232: public int getViewHits() {
233: return viewHits;
234: }
235:
236: /**
237: * Set the maximum number of hits returned.
238: * @see #getViewHits
239: */
240: public void setViewHits(int viewHits) {
241: this .viewHits = viewHits;
242: }
243:
244: /**
245: * Returns the current RDMServer variable.
246: * @since 3.01C
247: */
248: public String getRDMServer() {
249: return RDMServer;
250: }
251:
252: /**
253: * Sets the RDMServer variable.
254: * @param RDMServer The Compass server:port root, eg, http://compass.domain.com:111/
255: * @since 3.01C
256: */
257: public void setRDMServer(String RDMServer) {
258: this .RDMServer = RDMServer;
259: }
260:
261: /**
262: * Returns the current scope (query) for the search.
263: * @see #setScope
264: * @since 3.01C
265: */
266: public String getScope() {
267: return scope;
268: }
269:
270: /**
271: * Sets the scope (query) for the search.
272: * @see #getScope
273: * @since 3.01C
274: */
275: public void setScope(String scope) {
276: this .scope = scope;
277: }
278:
279: /**
280: * Returns the current RDMType.
281: * @see #setRDMType
282: * @since 3.01C
283: */
284: public String getRDMType() {
285: return RDMType;
286: }
287:
288: /**
289: * Sets the RDM Request type.
290: * @param RDMType Can be one of:
291: * <ul>
292: * <li>rd-request: The default request. Resource descriptions (documents).
293: * <li>taxonomy-request: Taxonomy.
294: * <li>schema-request: The schema.
295: * <li>server-request: Server information.
296: * <li>status-request: Server status information.
297: * </ul>
298: * @see #getRDMType
299: * @see #setQueryLanguage
300: * @since 3.01C
301: */
302: public void setRDMType(String RDMType) {
303: this .RDMType = RDMType;
304: }
305:
306: /**
307: * Returns the current query language.
308: * @see #setQueryLanguage
309: * @since 3.01C
310: */
311: public String getQueryLanguage() {
312: return ql;
313: }
314:
315: /**
316: * Sets the query language.
317: * @param ql Can be one of:
318: * <ul>
319: * <li>compass: The default Compass query language. Searches documents or the taxonomy.
320: * <li>taxonomy-basic: Used for requesting branches or parts of the taxonomy.
321: * <li>schema-basic: Queries the Compass schema.
322: * <li>url: Retrieves RDs by url (scope=url).
323: * </ul>
324: * @see #getQueryLanguage
325: * @see #setRDMType
326: * @since 3.01C
327: */
328: public void setQueryLanguage(String ql) {
329: this .ql = ql;
330: }
331:
332: /**
333: * Returns the SOIF attributes which are retrieved by a search.<BR>
334: * @return <code>viewAttributes</code> as set by setViewAttributes.
335: * A comma delimited list of attributes, returned by a search, eg<br>
336: * "score,title,description,url"<br>
337: * NB: a null string denotes that ALL SOIF attributes are returned.
338: * @see #setViewAttributes
339: * @since 3.01C
340: */
341: public String getViewAttributes() {
342: return viewAttributes;
343: }
344:
345: /**
346: * Sets the SOIF attributes which are returned for the search.
347: * @param viewAttributes A null string will return all attributes.
348: * A comma delimited list of attributes is accepted, eg<br>
349: * "score,title,description,url"
350: * @see #getViewAttributes
351: * @since 3.01C
352: */
353: public void setViewAttributes(String viewAttributes) {
354: this .viewAttributes = viewAttributes;
355: }
356:
357: /**
358: * Gets the sorting order for results.<BR>
359: * @param strVal A null string will return sorting according to
360: * the server default of -score (descending relevance). A comma
361: * delimited list of attributes is accepted, with + to denote
362: * ascending order and - to denote descending order, eg<br>
363: * "-score,+title"<br>
364: * @see #setViewOrder
365: * @since 3.01C
366: */
367: public String getViewOrder() {
368: return viewOrder;
369: }
370:
371: /**
372: * Sets the sorting order for results.<BR>
373: * @param viewOrder A null string will return sorting according to
374: * the server default of -score (descending relevance). A comma delimited
375: * list of attributes is accepted, with + to denote ascending order and - to
376: * denote descending order, eg<br>
377: * "-score,-title,+description"<br>
378: * @see #getViewOrder
379: * @since 3.01C
380: */
381: public void setViewOrder(String viewOrder) {
382: this .viewOrder = viewOrder;
383: }
384:
385: /**
386: * Return the fully formatted query url sent to the server.
387: */
388: public String getQuery() {
389: query = formatQuery();
390: return query;
391: }
392:
393: /**
394: * Execute the query.
395: * <p>Note: Query results are concatenated into a single StringBuffer.
396: * Use {@link #doQuery(int,int)} to process search results iteratively.
397: */
398: public void doQuery() {
399: if (status == COMPLETE)
400: status = PREPARED; /* allow for rerun */
401:
402: if (status == EMPTY)
403: checkPrepared(); /*can we allow this through?*/
404:
405: if (status != PREPARED)
406: return;
407:
408: status = PROCESSING;
409:
410: query = formatQuery();
411: rdmHeader = null;
412: soifResult = null;
413:
414: ReadConnection rc = new ReadConnection(query);
415:
416: if (streamMode) {
417: /* only set up the input stream and parse the rdm header */
418: if (!rc.openIn()) {
419: ReportError.reportError(ReportError.INTERNAL, "Search",
420: "doSearch", Messages.ABENDONREAD);
421: status = ABEND;
422: return;
423: }
424: resultStream = new DataInputStream(rc.getIS());
425:
426: SOIFParser sp = new SOIFParser(resultStream);
427: SOIF rdmhs = null;
428: try {
429: rdmhs = sp.parse();
430: } catch (IOException e) {
431: ReportError.reportError(ReportError.INTERNAL, "Search",
432: "doQuery", Messages.RDMERROR);
433: status = ABEND;
434: return;
435: }
436:
437: rdmHeader = new RDMHeader(rdmhs);
438:
439: String rdmEMsg = rdmHeader.getRDMErrorMessage();
440: if (rdmEMsg != null) {
441: if (!rdmEMsg.equalsIgnoreCase("0 results")) {
442: ReportError.reportError(ReportError.INTERNAL,
443: "Search", "doQuery", Messages.RDMERROR);
444: status = ABEND;
445: return;
446: }
447: }
448: return;
449: }
450:
451: if ((result = rc.doRead()) == null) {
452: ReportError.reportError(ReportError.INTERNAL, "Search",
453: "doSearch", Messages.ABENDONREAD);
454: status = ABEND;
455: return;
456: }
457:
458: if (!doSOIFParse) {
459: status = COMPLETE;
460: return;
461: }
462:
463: SOIFParser sp = new SOIFParser(result);
464:
465: if (!sp.isValid()) {
466: ReportError.reportError(ReportError.INTERNAL, "Search",
467: "doSearch", Messages.ABENDONINVALIDSOIF);
468: status = ABEND;
469: return;
470: }
471:
472: soifResult = sp.getSOIF();
473:
474: if (soifResult == null) {
475: ReportError.reportError(ReportError.INTERNAL, "Search",
476: "doQuery", Messages.NORDMHEADER);
477: status = ABEND;
478: return;
479: }
480:
481: rdmHeader = new RDMHeader(soifResult);
482:
483: String rdmEMsg = rdmHeader.getRDMErrorMessage();
484: if (rdmEMsg != null) {
485: if (!rdmEMsg.equalsIgnoreCase("0 results")) {
486: ReportError.reportError(ReportError.INTERNAL, "Search",
487: "doQuery", Messages.RDMERROR);
488: status = ABEND;
489: return;
490: }
491: }
492:
493: status = COMPLETE;
494: }
495:
496: /**
497: * Execute the query, returning viewHits hits starting at firstHit.
498: */
499: public void doQuery(int firstHit, int viewHits) {
500: setFirstHit(firstHit);
501: setViewHits(viewHits);
502: doQuery();
503: }
504:
505: /**
506: * Return result. Returns null if the status is not
507: * COMPLETE. Note that an ABEND returns a null.
508: */
509: public String getStringResult() {
510: if (status != COMPLETE) {
511: return null;
512: }
513: return result;
514: }
515:
516: /**
517: * Return SOIF version of result. Returns null if the
518: * status is not COMPLETE. Note that an ABEND returns
519: * a null.
520: */
521: public SOIF getSOIFResult() {
522: if (status != COMPLETE) {
523: return null;
524: }
525: return soifResult.next; /* skip the rdm header */
526: }
527:
528: /**
529: * Return the SOIF RDM result header. Returns null if the
530: * status is not COMPLETE. Note that an ABEND returns
531: * a null.
532: */
533: public SOIF getRDMHeaderSOIF() {
534: if (status != COMPLETE) {
535: return null;
536: }
537: return soifResult;
538: }
539:
540: /**
541: * Return results as a DataInputStream.
542: * Can only be used if stream mode is enabled.
543: * @since 3.01C
544: * @see #setStreamMode
545: */
546: public DataInputStream getResultStream() {
547: /* XXX status is PROCESSING here */
548: return streamMode ? resultStream : null;
549: }
550:
551: /**
552: * The number of results returned by this search.
553: * Returns -1 on error.
554: *
555: * The result count is based on the RDM header SOIF,
556: * so if doSOIFParse is false, -1 will be returned.
557: * @deprecated 3.01C, see {@link #getResultCount()}
558: */
559: public int resultCount() {
560: if (rdmHeader != null)
561: return rdmHeader.resultCount();
562: else
563: return -1;
564: }
565:
566: /**
567: * The number of results returned by the search.
568: * Returns -1 on error.
569: *
570: * The result count is based on the RDM header SOIF,
571: * so if doSOIFParse is false, -1 will be returned.
572: * @since 3.01C
573: */
574: public int getResultCount() {
575: if (rdmHeader != null)
576: return rdmHeader.resultCount();
577: else
578: return -1;
579: }
580:
581: /**
582: * The total number of results that matched the search.
583: * @deprecated 3.01C, see {@link #getHitCount()}
584: */
585: public int hitCount() {
586: if (rdmHeader != null)
587: return rdmHeader.hitCount();
588: else
589: return 0;
590: }
591:
592: /**
593: * The total number of results that matched the search.
594: * @since 3.01C
595: */
596: public int getHitCount() {
597: if (rdmHeader != null)
598: return rdmHeader.hitCount();
599: else
600: return 0;
601: }
602:
603: /**
604: * The total number of documents available to be searched.
605: * @deprecated 3.01C, see {@link #getDocumentCount()}
606: */
607: public int documentCount() {
608: if (rdmHeader != null)
609: return rdmHeader.documentCount();
610: else
611: return 0;
612: }
613:
614: /**
615: * The total number of documents available to be searched.
616: * @since 3.01C
617: */
618: public int getDocumentCount() {
619: if (rdmHeader != null)
620: return rdmHeader.documentCount();
621: else
622: return 0;
623: }
624:
625: /**
626: * Return processing status.
627: */
628: public final int getStatus() {
629: return status;
630: }
631:
632: /**
633: * Return whether or not processing is complete.
634: * @deprecated 3.01C, see {@link #isFinished()}
635: */
636: public final boolean finished() {
637: return (status == COMPLETE || status == ABEND);
638: }
639:
640: /**
641: * Return whether or not processing is complete.
642: * @since 3.01C
643: */
644: public final boolean isFinished() {
645: return (status == COMPLETE || status == ABEND);
646: }
647:
648: /**
649: * Checks to see if EMPTY is PREPARED by checking required paramaters.<BR>
650: * @since 3.01C
651: */
652: private void checkPrepared() {
653: if (status == EMPTY) {
654: if (RDMServer != null && RDMType != null && ql != null
655: && scope != null) {
656: status = PREPARED;
657: doSOIFParse = true;
658: }
659: }
660: }
661:
662: /**
663: * Return debug string version of Search instance.
664: */
665: public String toString() {
666: return "Search instance:\n" + "\tRDMType = [" + RDMType
667: + "]\n" + "\tquery = [" + query + "]\n"
668: + "\tscope = [" + scope + "]\n"
669: + "\tviewAttributes = [" + viewAttributes + "]\n"
670: + "\tviewOrder = [" + viewOrder + "]\n"
671: + "\tviewHits = [" + firstHit + ".."
672: + (firstHit + viewHits - 1) + "]\n"
673: + "\tstatus = [" + status + "]\n";
674: }
675:
676: /**
677: * Format the query string using passed view-hits. This enables
678: * you to break large queries into multiple smaller queries.
679: */
680: private String formatQuery() {
681: if (!RDMServer.endsWith("/"))
682: RDMServer += "/";
683: return RDMServer
684: + "rdm/incoming"
685: + "?type="
686: + this .RDMType
687: + "&ql="
688: + this .ql
689: + (this .csid != null ? "&csid="
690: + java.net.URLEncoder.encode(this .csid) : "")
691: + "&scope=" + java.net.URLEncoder.encode(this .scope)
692: + "&view-attributes="
693: + java.net.URLEncoder.encode(this .viewAttributes)
694: + "&view-order=" + this .viewOrder + "&view-hits="
695: + firstHit + ".." + (firstHit + viewHits - 1);
696: }
697:
698: }
|