package edu.vt.marian.WebGate; import java.net.*; import java.util.*; import java.io.*; import edu.vt.marian.common.*; import edu.vt.marian.uip.*; /** Class name: Results Class description: this class represent the results of a query Author: Jianxin Zhao Finished time: ????, 1998 Known bugs: none Platform: jdk1.1.5 under UNIX ------------------------------------------------------------------------ Modified By: Robert France Date Modified: 9 Nov 2000 Platform Modified With: UNIX Reason For Modification: Added method to return short description list in HTML. This replaces about 87 duplicate code segments in Response.java. Note use of htmlLinkPrefix. */ public class Results { /** this is just for debugging */ Debug debug; /** this is the servers the documents of this object come from */ private Vector servers; /** this is the states of each servers */ private Vector states; /** this is the documents of this object */ private Vector documents; /** this is the index of documents, it's used to identify which document come from which server */ private Vector indexes; /** this is the current status of this Results object, it's used for circulation information only */ private String status; /** this variable contains the circulation information of this object */ private String circulation; /** this flag indicates wether or not the content of this object has been changed since it's read out from a directory */ private boolean changed; /** this is the directory from which this object is read out */ private String dir; // there need to be another vector which will record the time when the // result from each server are ready, this will be implemented in the future /** this constructor will create a Results object, the object is empty upon creation */ public Results(Vector server_list, Debug debug) { // first initialize data members init(); this.debug = debug; if (server_list != null) { int i; for (i = 0; i < server_list.size(); i++) { servers.addElement(server_list.elementAt(i)); } } // set state for each server int i; String state = "INIT"; for (i = 0; i < servers.size(); i++) { states.addElement(state); } // since this is the first time this object is created, there is the need // to write it back to disk when method save() is called changed = true; } /** this constructor will create a Results object from the specified directory/file */ public Results(String dir, Debug debug) { // first initialize data members init(); this.debug = debug; BufferedReader br; FileReader fr; int i; int number_servers = 0; int number_documents = 0; // read out the status and circulation information of this object try { fr = new FileReader(dir); br = new BufferedReader(fr); set_status(br.readLine()); // circulation information, multiple lines allowed int number_lines = Integer.parseInt(br.readLine()); String s = new String(""); for (i = 0; i < number_lines; i++) { if (i == 0) { // the first line s += br.readLine(); } else { // not the first line, add "\n" between them s += "\n" + br.readLine(); } } if (i != 0) { set_circulation(s); } // read out servers // read out number of servers number_servers = Integer.parseInt(br.readLine()); // read out each server uip_server us; for (i = 0; i < number_servers; i++) { us = new uip_server(br, debug); servers.addElement(us); } // read out states // read out state of each server for (i = 0; i < number_servers; i++) { states.addElement(br.readLine()); } // read out documents, note I don't think that we should save the whole // document to disk, that's too space consuming, we may only save // document id in the future // read out number of documents number_documents = Integer.parseInt(br.readLine()); // read out each document WtdEntireObj doc; for (i = 0; i < number_documents; i++) { doc = new WtdEntireObj(br, debug); documents.addElement(doc); } // read out indexes // read out index of each document for (i = 0; i < number_documents; i++) { indexes.addElement(new Integer(br.readLine())); } br.close(); } catch (IOException e3) { debug.dumpTrace("Results(dir): error reading data from " + dir); } this.dir = new String(dir); // since we are sure here that the contewnt of this object is the same as that // in the directory from which it's read out, no need to write it to disk // when method save() is called changed = false; } /** this mehod will create a Results object from a stream, this might be helpful for remote management in the future . (not implemented yet) */ public Results(DataInputStream dis, Debug debug) { } /** this method will return the current servers this object is corresponds to */ public Vector get_server_list() { Vector server_list = new Vector(); int i; for (i = 0; i < servers.size(); i++) { server_list.addElement(servers.elementAt(i)); } return server_list; } /** this method will return the uip server from which the specified document came */ public uip_server get_server_by_doc_number(String doc_number) { int index = Integer.parseInt(doc_number); return( get_server_by_index(index) ); } public uip_server get_server_by_index(int index) { if ((index < 0) || (index >= indexes.size())) //**DEVEL: Should { // this check be moved to // doc number is out of range // the routine above? return null; } // valid doc number int server_no = ((Integer) indexes.elementAt(index)).intValue() ; return (uip_server) servers.elementAt(server_no); } /** this method will store a number of documents from the specified uip server to the Results object, the parameter more_to_come specifys whether or not this is the last bounch documents come from that server */ public synchronized String store_docs(uip_server us, Vector docs, String more_to_come) { // first get the index of this server int i; uip_server my_us; for (i = 0; i < servers.size(); i++) { my_us = (uip_server) servers.elementAt(i); if (my_us.equals(us).equals("yes")) { // find a match, store it's documents and set indexes int j; for (j = 0; j < docs.size(); j++) { documents.addElement(docs.elementAt(j)); indexes.addElement(new Integer(i)); } // set the state of this server Vector uip_servers = new Vector(); uip_servers.addElement(us); if (more_to_come.equals("no")) { // all the documents from this server are ready set_status(uip_servers, "QUERY_COMPLETE"); } else { // only part of the documents from this server // arrived set_status(uip_servers, "QUERY_DONE"); } changed = true; return "OK"; } } // this is no such server return "no such server"; } /** this method will set the circulation information of this object */ public String set_circulation(String circulation) { changed = true; if (circulation == null) { this.circulation = null; set_status("CIRCULATION_DONE"); return "ok"; } this.circulation = new String(circulation); set_status("CIRCULATION_DONE"); return "ok"; } /** this method will return the circulation information of this object */ public String get_circulation() { if (circulation == null) { return null; } return new String(circulation); } /** this method will set the status of this object */ public String set_status(String s) { changed = true; status = s; return "ok"; } /** this method will return the current status of this Results object status will tell something like whether or not all documents are ready. */ public String get_status() { return status; } /** this method will set the status of the specified servers to the specified value */ public synchronized String set_status(Vector uip_servers, String s) { int i, j; uip_server server1, server2; if (s.equals("INIT") || s.equals("QUERY") || s.equals("QUERY_DONE") || s.equals("QUERY_COMPLETE")) { // only these states are supported currently for (i = 0; i < uip_servers.size(); i++) { server1 = (uip_server) uip_servers.elementAt(i); for (j = 0; j < servers.size(); j++) { server2 = (uip_server) servers.elementAt(j); if (server1.equals(server2).equals("yes")) { // a match is found changed = true; states.setElementAt(s, j); break; } } } return "ok"; } // invalid state is encountered debug.dumpTrace("Results.set_status(servers, state): invalid state '" + s + "'."); return "invalid state"; } /** this method will return the status of documents corresponds to the specified server */ public String get_status(uip_server us) { int i; uip_server my_us; for (i = 0; i < servers.size(); i++) { my_us = (uip_server) servers.elementAt(i); if (my_us.equals(us).equals("yes")) { // a match is found, return the server's state return (String) states.elementAt(i); } } // there is no such server return "no such server"; } /** this method will tell whether or not all the servers of this object is in the specified state */ public boolean all_in_state(String state) { int i; String s; if (state == null) { // check to see if all the servers are in state null for (i = 0; i < states.size(); i++) { s = (String) states.elementAt(i); if (s != null) { // some server has non null state return false; } } // all servers are in state null return true; } for (i = 0; i < states.size(); i++) { s = (String) states.elementAt(i); if ((s == null) || (! s.equals(state))) { // find a server which is not in the specified state return false; } } // all servers are in the specified state return true; } /** this method will return the servers which are in the specified state */ public Vector get_servers_in_state(String state) { int i; String s; Vector server_list = new Vector(); if (state == null) { // find all servers which are in state null for (i = 0; i < states.size(); i++) { s = (String) states.elementAt(i); if (s == null) { server_list.addElement(servers.elementAt(i)); } } return server_list; } for (i = 0; i < states.size(); i++) { s = (String) states.elementAt(i); if ((s != null) && (s.equals(state))) { // find a matched server server_list.addElement(servers.elementAt(i)); } } return server_list; } /** this method will return the time when the result is ready, it's helpful for system performance measure. (not implemented yet) */ public String get_ready_time() { return null; // not implemented yet } /** this method will return the time when the result from the specified server is ready. (not implemented yet) */ public String get_ready_time(uip_server us) { return null; // not implemented yet } /** this method will return the number of documents in this object. */ public String get_number_docs() { return Integer.toString(documents.size()); } /** this method will the number of documents in this object which match the specified query data. (not implemented yet) */ public String get_number_docs(general_query_data gqd) { return null; // not implemented yet } /** this method will return the number of documents in this object about the specified uip server, note the documents in a Results object may come from different servers, the meaning of the return value is the same as the above methods */ public String get_number_docs(uip_server us) { // first find the index of the server int i; int server_index = -1; uip_server my_us; for (i = 0; i < servers.size(); i++) { my_us = (uip_server) servers.elementAt(i); if (my_us.equals(us).equals("yes")) { // find a match, record it's index server_index = i; break; } } if (server_index == -1) { // this is no such server return "no such server"; } // return number of documents which come from the server int num_docs = 0; for (i = 0; i < indexes.size(); i++) { if (((Integer)indexes.elementAt(i)).intValue() == server_index) { // this document comes from this server num_docs++; } } return Integer.toString(num_docs); } /** this method will the number of documents of this object which come from the specified server and also match the specified query data */ public String get_number_docs(uip_server us, general_query_data gqd) { return null; // not implemented yet } /** this method will return the document at the specified index of this Results object */ public WtdEntireObj get_doc(String index) { int i = Integer.parseInt(index); if ((i < 0) || (i >= documents.size())) { // invalid index return null; } // valid index, return the corresponding document return (WtdEntireObj) documents.elementAt(i); } /** this method will return the document at the specified index of this Results object which come from the specified uip server */ public WtdEntireObj get_doc(uip_server us, String index) { int i = Integer.parseInt(index); if (i < 0) { // invalid index return null; } // find the index of the server int j; int server_index = -1; uip_server my_us; for (j = 0; j < servers.size(); j++) { my_us = (uip_server) servers.elementAt(j); if (my_us.equals(us).equals("yes")) { // find a match, record it's index server_index = j; break; } } if (server_index == -1) { // this is no such server return null; } // return number of documents which come from the server int num_docs = 0; for (j = 0; j < indexes.size(); j++) { if (((Integer)indexes.elementAt(j)).intValue() == server_index) { // this document comes from this server if (num_docs == i) { // this is the document we want return (WtdEntireObj) documents.elementAt(j); } num_docs++; } } // index too large debug.dumpTrace("Results.get_doc(): index " + index + " too large: max possible is " + Integer.toString(j) + "."); return null; } /** this method will return all the documents currently contained in this Results object */ public Vector get_docs() { return documents; } /** this method will the documents of this object which match the specified query data. (not implemented yet) */ public String get_docs(general_query_data gqd) { return null; // not implemented yet } /** this method will return all the documents currently contained in this Results object which come from the specified uip server */ public Vector get_docs(uip_server us) { // first find the index of the server int i; int server_index = -1; uip_server my_us; for (i = 0; i < servers.size(); i++) { my_us = (uip_server) servers.elementAt(i); if (my_us.equals(us).equals("yes")) { // find a match, record it's index server_index = i; break; } } if (server_index == -1) { // this is no such server return null; } // find the documents which come from this server Vector docs = new Vector(); int j; for (i = 0; i < indexes.size(); i++) { j = ((Integer) indexes.elementAt(i)).intValue(); if (j == server_index) { // this document comes from this server docs.addElement(documents.elementAt(i)); } } // return the documents we found return docs; } /** this method will the documents of this object which come from the specified server and also match the specified query data */ public String get_docs(uip_server us, general_query_data gqd) { return null; // not implemented yet } /** this method will return all the documents from the start index to the end index in this Results object */ public Vector get_docs(String start_index, String end_index) { int start = Integer.parseInt(start_index); int end = Integer.parseInt(end_index); if ((start < 0) || (start > documents.size()) || (end <0) || (end > documents.size())) { // index is invalid return null; } if (start > end) { return null; } // find the documents and return them Vector docs = new Vector(); int i; for (i = start; i <= end; i++) { docs.addElement((WtdEntireObj) documents.elementAt(i)); } return docs; } /** this method will return all the documents from the start index to the end index coresponds to the specified uip server in this Results object (not implemented yet) */ public Vector get_docs(uip_server us, String start_index, String end_index) { return null; // not implemented yet } /** Return an HTML string containing an ordered list of short descriptions for all the documents in this object. */ public String shortDescListInHtml(String htmlLinkPrefix) { // show all the documents StringBuffer sb = new StringBuffer(documents.size()*200); sb.append("\n
    \n"); int i; for (i = 0; i < documents.size(); i++) { sb.append(docDescInHtmlByIndex(i, htmlLinkPrefix)); } sb.append("
\n"); return( sb.toString() ); } public String shortDescListInHtml(String htmlLinkPrefix, int batchSize, int startIndex, int endIndex) { debug.dumpTrace("Results.shortDescListInHtml(): extracting documents between " + startIndex + "-" + endIndex + "."); if ( (startIndex < 0) || (startIndex >= documents.size()) || (endIndex < startIndex) || (endIndex >= documents.size()) ) { debug.dumpTrace("Results.shortDescListInHtml(): cannot extract documents between " + startIndex + "-" + endIndex + ": index(es) not valid. Ignoring request."); return( "" ); } // show the documents StringBuffer sb = new StringBuffer((endIndex-startIndex)*200); sb.append("\n
    \n"); int i; boolean showBlocks = true; if ( (startIndex == 0) && (endIndex > batchSize) ) showBlocks = false; for (i = startIndex; i <= endIndex; i++) { if ( showBlocks && ((i%batchSize) == 0) ) { sb.append("

    Documents "); sb.append(i+1); sb.append(" - "); if ( (endIndex-i) < batchSize) sb.append(endIndex+1); else sb.append(i+batchSize); sb.append("

    \n"); } sb.append(docDescInHtmlByIndex(i, htmlLinkPrefix)); } sb.append("

\n"); return( new String(sb) ); } private StringBuffer docDescInHtmlByIndex(int index, String htmlLinkPrefix) { StringBuffer sb = new StringBuffer(200); WtdEntireObj weo = (WtdEntireObj) documents.elementAt(index); debug.dumpTrace("Results.docDescInHtmlByIndex(): doc is " + weo.toString() + "."); ////DEBUG Document doc = weo.getDocument(); if ( doc == null) { debug.dumpTrace("Results.docDescInHtmlByIndex(): Doc '" + weo.toString() + "' cannot be extracted: ignoring."); return( sb ); // Currently the empty string. } sb.append("
  • "); sb.append(htmlLinkPrefix); sb.append("/doc_number_"); sb.append(index); sb.append("=on\">"); sb.append(doc.presentShort(DigInfObj.HTML)); sb.append(""); sb.append(" -- from "); uip_server us = get_server_by_index(index); sb.append(us.get_short_description()); sb.append("
    \n"); return( sb ); } /** this method will return the short descriptions of the specified documents in html format. (not implemented yet) */ public String get_short_description_in_html(Vector doc_indexes) { return null; // not implemented yet } /** this method will return the short descriptions of the specified documents in html format. (not implemented yet) */ public String get_short_description_in_html(String start_index, String end_index) { return null; // not implemented yet } /*** this method will return the short descriptions of the specified documents which come from the specified server in html format. (not implemented yet) */ public String get_short_description_in_html(uip_server us, Vector doc_indexes) { return null; // not implemented yet } /** this method will return the short descriptions of the specified documents which come from the specified server in html format. (not implemented yet) */ public String get_short_description_in_html(uip_server us, String start_index, String end_index) { return null; // not implemented yet } /** theis method will return the long description of the specified documents in html format. (not implemented yet) */ public String get_long_description_in_html(Vector doc_indexes) { return null; // not implemented yet } /** theis method will return the long description of the specified documents in html format. (not implemented yet) */ public String get_long_description_in_html(String start_index, String end_index) { return null; // not implemented yet } /** theis method will return the long description of the specified documents which come from the specified server in html format. (not implemented yet) */ public String get_long_description_in_html(uip_server us, Vector doc_indexes) { return null; // not implemented yet } /** theis method will return the long description of the specified documents which come from the specified server in html format. (not implemented yet) */ public String get_long_description_in_html(uip_server us, String start_index, String end_index) { return null; // not implemented yet } /** this method will print the content of this object to a stream, this might be useful for remote management in the future. (not implemented yet) */ public String to_stream(DataOutputStream dos) { return null; // not implemented yet } /** this method will save the content of this object to the specified file */ public String save(String dir) { if ((this.dir != null) && this.dir.equals(dir) && (! changed)) { // user want to write the information back to the same directory // from which this object is read out, yet the content of this // object has not been changed, so do nothing here // debug.dumpTrace("content unchanged, no write to disk"); return "ok"; } // create the stream PrintWriter pw = null; try { pw = new PrintWriter(new FileWriter(dir)); } catch (Exception e) { debug.dumpTrace("Results.save(): error opening file to write"); } // save configuration -- including object status and circulation // information // write status pw.println(get_status()); // write circulation information, multiple lines allowed LinedString ms = new LinedString(debug); String s = get_circulation(); if (s == null) { pw.println("0"); } else { pw.println(ms.count_lines(s)); pw.println(s); } // save servers // write number of servers pw.println(servers.size()); // write each server int i; uip_server us; for (i = 0; i < servers.size(); i++) { us = (uip_server) servers.elementAt(i); us.to_stream(pw); } // save states // write state of each server for (i = 0; i < states.size(); i++) { pw.println((String) states.elementAt(i)); } // save documents // write number of documents pw.println(documents.size()); // write each document WtdEntireObj doc; for (i = 0; i < documents.size(); i++) { doc = (WtdEntireObj) documents.elementAt(i); doc.toStream(pw); } // save indexes // write index of each document for (i = 0; i < indexes.size(); i++) { pw.println(((Integer)indexes.elementAt(i)).toString()); } // at last flush and close the stream pw.flush(); pw.close(); if ((this.dir != null) && this.dir.equals(dir)) { // we are sure here that the content of this object is the same as // that in the directory from which it's read out changed = false; } // debug.dumpTrace("content changed, write to disk"); return "OK"; } /** this method will initialize the data members of this object */ private void init() { debug = null; status = new String("INIT"); circulation = null; servers = new Vector(); states = new Vector(); indexes = new Vector(); documents = new Vector(); dir = null; } }