/*************************************************************************
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: SearchState.java,v $
*
* $Revision: 1.2 $
*
* last change: $Author: rt $ $Date: 2005/09/09 16:37:28 $
*
* The Contents of this file are made available subject to
* the terms of GNU Lesser General Public License Version 2.1.
*
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2005 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
************************************************************************/
package com.sun.xmlsearch.servlet;
import java.io.*;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.util.StringTokenizer;
import javax.swing.tree.TreeNode;
import javax.servlet.*;
import javax.servlet.http.*;
// for configuration
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.net.URL;
import com.sun.xmlsearch.util.Configuration;
import com.sun.xmlsearch.util.ServiceFinder;
import com.sun.xmlsearch.tree.*;
import com.sun.xmlsearch.xml.qe.*;
final class SearchState {
private final class ServiceNode implements TreeNode {
private QueryProcessor _service;
private String _serviceName;
private ServiceNode _parent;
private Vector _children = new Vector();
public ServiceNode(String name, QueryProcessor service) {
_serviceName = name;
_service = service;
_expandedServiceNodes.put(name, name); // all expanded at first
}
public void collectServices(Vector result) throws Exception {
if (_service != null) {
result.addElement(_service);
_collectionClassification = _service.getClassification();
}
for (int i = 0; i < _children.size(); i++)
((ServiceNode)_children.elementAt(i)).collectServices(result);
}
public QueryProcessor getService() {
return _service;
}
public void addChild(ServiceNode node) {
_children.addElement(node);
}
public String toString() {
return _serviceName;
}
public TreeNode getParent() {
return _parent;
}
public boolean getAllowsChildren() {
return true;
}
public boolean isLeaf() {
return getChildCount() == 0;
}
public int getChildCount() {
return _children.size();
}
public TreeNode getChildAt(int i) {
return (TreeNode)_children.elementAt(i);
}
public int getIndex(final TreeNode node) {
return isLeaf() ? -1 : _children.indexOf(node);
}
public Enumeration children() {
return _children.elements();
}
} // end of ServiceNode
private QueryProcessor[] _queryServices;
private ServiceNode _serviceTree;
private String _collectionClassification = "java";
private QueryProcessor _queryProcessor;
private Vector _engines = new Vector();
private DocumentServer _docServer;
private QueryResults _currentQueryResults;
private Hashtable _tocTreeCache = new Hashtable();
private Hashtable _docRequests = new Hashtable();
private Hashtable _docFragments = new Hashtable();
private Hashtable _expandedNodes = new Hashtable();
private Hashtable _selectedNodes = new Hashtable();
private Hashtable _hitUrlStrings = new Hashtable();
private Hashtable _serviceNodes = new Hashtable();
private Hashtable _expandedServiceNodes = new Hashtable();
private String _lastQuery = "";
private String _lastScope = "";
private String _currentDocUrl;
private String _currentDocType;
private TocTree.TocNode _currentTocRoot;
private TocTree.TocNode _currentScopeNode;
private ServiceNode _currentServiceNode;
public static synchronized SearchState getSearchState(HttpServletRequest req) {
String date = (new java.util.Date()).toString();
HttpSession session = req.getSession(true);
System.out.println(date + ": " + session.getId());
Object object = session.getValue(session.getId());
if (object == null) {
System.out.println("new session with " + req.getRemoteHost());
SearchState state = new SearchState();
state.init();
session.putValue(session.getId(), state);
return state;
}
else
return (SearchState)object;
}
private DocumentFragment fetchDocumentFragment(String key) {
return (DocumentFragment)_docFragments.get(key);
}
private void init() {
try {
URL cfgUrl = new URL("http://bigblock.east:8084/remoteSearchServices.xml");
Element cfg = Configuration.parse(cfgUrl);
if (cfg != null)
configureSearchServer(cfg);
_queryServices = getProcessors();
buildServiceTree(_queryServices);
_docServer = DocumentServer.instance();
}
catch (Exception e) {
e.printStackTrace();
}
}
private QueryProcessor[] getProcessors() {
QueryProcessor[] result = new QueryProcessor[_engines.size()];
if (_engines.size() > 0)
result = (QueryProcessor[])_engines.toArray(result);
return result;
}
private void configureSearchServer(Element config) throws Exception {
if (config.getTagName().equals("XmlQueryProcessors")) {
NodeList list = config.getElementsByTagName("XmlSearchEngine");
for (int i = 0; i < list.getLength(); i++) {
Element seConfig = (Element)list.item(i);
QueryProcessorImpl qp = new QueryProcessorImpl();
if (qp.init(seConfig))
_engines.addElement(qp);
}
// process remote services
list = config.getElementsByTagName("XmlSearchService");
if (list.getLength() > 0) {
for (int i = 0; i < list.getLength(); i++) {
Element clientConfig = (Element)list.item(i);
System.out.println(clientConfig);
ServiceFinder finder = new ServiceFinder(_engines, "XmlSearchService");
// finder.configure(clientConfig);
new Thread(finder).start();
while (finder.getNumberOfServices() == 0)
Thread.sleep(500);
}
}
setupQueryProcessor(_engines);
}
else
throw new Exception("inappropriate config element for GuiDemo");
}
private void setupQueryProcessor(Vector engines) throws Exception {
switch (engines.size()) {
case 0:
System.err.println("no QueryProcessors!");
break;
case 1:
Object whatIsIt = engines.elementAt(0);
if (whatIsIt instanceof XmlSearchServer) // remote
_queryProcessor = (XmlSearchServer)whatIsIt;
else if (whatIsIt instanceof QueryProcessor)
_queryProcessor = (QueryProcessor)whatIsIt;
else
throw new Exception("unknown service type");
break;
default:
QueryHitMerger merger = new QueryHitMerger();
for (int i = 0; i < engines.size(); i++)
merger.addSearchServer((XmlSearchServer)engines.elementAt(i));
_queryProcessor = merger;
break;
}
}
private void buildServiceTree(QueryProcessor[] processors) {
try {
ServiceNode root = new ServiceNode("all collections", null);
for (int i = 0; i < processors.length; i++) {
QueryProcessor proc = processors[i];
StringTokenizer topics = new StringTokenizer(proc.getClassification(),"/");
ServiceNode currentNode = root;
while (true) {
String topic = topics.nextToken();
if (topics.hasMoreTokens()) {
ServiceNode topicNode = (ServiceNode)_serviceNodes.get(topic);
if (topicNode == null) {
topicNode = new ServiceNode(topic, null);
_serviceNodes.put(topic, topicNode);
currentNode.addChild(topicNode);
}
currentNode = topicNode;
}
else {
ServiceNode topicNode = new ServiceNode(topic, proc);
_serviceNodes.put(topic, topicNode);
currentNode.addChild(topicNode);
break;
}
}
}
_serviceTree = root;
}
catch (Exception e) {
e.printStackTrace();
}
}
private DocumentRequest getDocRequest(String document, String docType) {
DocumentRequest req = (DocumentRequest)_docRequests.get(document);
return req != null ? req : new DocumentRequest(document, docType);
}
public synchronized void retrieveDocumentFragment(String hit) {
try {
DocumentFragment fragment = fetchDocumentFragment(hit);
if (fragment == null) {
int index = Integer.parseInt(hit);
QueryHitData hitData = _currentQueryResults.getHit(index);
_currentDocUrl = hitData.getDocument();
_currentDocType = hitData.getDocumentType();
DocumentRequest request = getDocRequest(_currentDocUrl,
_currentDocType);
request.setFocus(hitData.getLocator());
fragment = getDocumentFragment(request);
_docFragments.put(hit, fragment);
_hitUrlStrings.put(hit, _currentDocUrl);
TocTree.TocNode[] tocPath = fragment.nodes();
_currentTocRoot = tocPath[0];
// setup a set of nodes expanded to show the location of the hit
_expandedNodes.clear();
_selectedNodes.clear();
_expandedNodes.put("/doc", this);
for (int i = 1; i < tocPath.length; i++) {
String xPath = tocPath[i].getXPath();
_expandedNodes.put(xPath, this);
_selectedNodes.put(xPath, this);
}
}
else
_currentDocUrl = (String)_hitUrlStrings.get(hit);
}
catch (Exception e) {
e.printStackTrace();
}
}
// doc name can be of the MAN: type, etc.
public synchronized void retrieveDocument(String docName) {
try {
URL docUrl = _docServer.findDocUrl(docName);
_currentDocUrl = docUrl.toString();
if (fetchDocumentFragment(docName) == null) {
DocumentRequest request = getDocRequest(_currentDocUrl,
_currentDocType);
request.setFocus(new MultiTokenLocator("/doc"));
DocumentFragment fragment = getDocumentFragment(request);
_docFragments.put(docName, fragment);
TocTree.TocNode[] tocPath = fragment.nodes();
_currentTocRoot = tocPath[0];
_expandedNodes.clear();
_selectedNodes.clear();
_expandedNodes.put("/doc", this);
// direct children
for (int i = 0; i < tocPath[0].getChildCount(); i++) {
TocTree.TocNode child = (TocTree.TocNode)tocPath[0].getChildAt(i);
_expandedNodes.put(child.getXPath(), this);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void retrieveHitArea(String hit, OutputStream out) {
try {
DocumentFragment fragment = fetchDocumentFragment(hit);
System.out.println(fragment);
out.write(fragment.getHTML());
}
catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void retrieveDocumentFragment(String xPath, OutputStream out) {
try {
DocumentRequest request = getDocRequest(_currentDocUrl,
_currentDocType);
request.setFocus(new MultiTokenLocator(xPath));
DocumentFragment fragment = getDocumentFragment(request);
out.write(fragment.getHTML());
}
catch (Exception e) {
e.printStackTrace();
}
}
private static int[] Widths = {15, 15, 25, 20, 20, 15, 15, 875};
private void treeTableHeader(PrintWriter out) {
out.write("<table cellspacing=0 cellspadding=0 border=0 width=1000><tr>");
for (int i = 0; i < Widths.length; i++) {
out.write("<th width=");
out.print(Widths[i]);
out.write('>');
}
out.write("</tr>");
}
public synchronized void getServices(PrintWriter out) {
out.write("<h2>Select collection</h2>");
treeTableHeader(out);
walkServiceTree(1, _serviceTree, out);
out.write("</table>");
}
public synchronized void expandServices(String name, PrintWriter out) {
_expandedServiceNodes.put(name, name);
getServices(out);
}
public synchronized void collapseServices(String name, PrintWriter out) {
_expandedServiceNodes.remove(name);
getServices(out);
}
public synchronized void activateNode(String name, PrintWriter out) {
try {
_lastScope = "";
ServiceNode node = (ServiceNode)_serviceNodes.get(name);
Vector engines = new Vector();
node.collectServices(engines);
setupQueryProcessor(engines);
getScopeModel(out);
}
catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void getScopeModel(PrintWriter out) {
try {
TocTree.TocNode cmodel =
_docServer.getCollectionModel(_collectionClassification).getTreeRoot();
out.write("<h2>Select scope</h2>");
treeTableHeader(out);
walkScopesTree(1, cmodel, out);
out.write("</table>");
}
catch (Exception e) {
e.printStackTrace();
}
}
private void walkScopesTree(int level, TocTree.TocNode node, PrintWriter out) {
out.write("<tr><td colspan=\"");
out.print(level);
out.write("\">");
if (node == _currentScopeNode) {
out.write("<a name=\"CurrentPos\">");
out.write("<img src=\"/icons/ab2_toc_index.gif\" border=0 width=18 height=17>");
out.write("</a>");
}
out.write("</td><td> </td><td></td><td colspan=\"");
out.print(6 - level);
out.write("\" nowrap><a href=\"/servlet/query?scope=");
out.write(node.getXPath());
out.write("\" target=\"results\">");
out.write(node.getTitle());
out.write("</a></td></tr>");
if (!node.isLeaf()) {
final int nChildren = node.getChildCount();
for (int i = 0; i < nChildren; i++)
walkScopesTree(level + 1, (TocTree.TocNode)node.getChildAt(i), out);
}
}
private void walkServiceTree(int level, ServiceNode node, PrintWriter out) {
final String name = node.toString();
final boolean toExpand = _expandedServiceNodes.get(name) != null;
out.write("<tr><td colspan=\"");
out.print(level);
out.write("\"></td><td> </td>");
if (node.isLeaf())
out.write("<td></td>");
else {
out.write("<td><a href=\"/servlet/services?");
out.write(toExpand ? "collapse=" : "expand=");
out.write(name);
out.write("\"><img src=\"/icons/");
out.write(toExpand ? "ab2_minus" : "ab2_plus");
out.write(".gif\" border=0 width=21 height=17></a></td>");
}
out.write("<td colspan=\"");
out.print(6 - level);
out.write("\" nowrap><a href=\"/servlet/services?node=");
out.write(name);
out.write("\" target=\"scopes\">");
out.write(name);
out.write("</a></td></tr>");
if (toExpand && !node.isLeaf()) {
final int nChildren = node.getChildCount();
for (int i = 0; i < nChildren; i++)
walkServiceTree(level + 1, (ServiceNode)node.getChildAt(i), out);
}
}
public synchronized void expandToc(String xPath, PrintWriter out) {
_expandedNodes.put(xPath, this);
retrieveToc(out);
}
public synchronized void collapseToc(String xPath, PrintWriter out) {
_expandedNodes.remove(xPath);
retrieveToc(out);
}
public synchronized void retrieveToc(String key, PrintWriter out) {
DocumentFragment fragment = fetchDocumentFragment(key);
_currentTocRoot = fragment.getTocTree().getRoot();
retrieveToc(out);
}
public synchronized void retrieveToc(PrintWriter out) {
treeTableHeader(out);
walkTocTree(1, _currentTocRoot, out);
out.write("</table>");
}
private void walkTocTree(int level, TocTree.TocNode node, PrintWriter out) {
String xPath = node.getXPath().trim();
if (xPath.length() == 0)
xPath = "/doc";
final boolean toExpand = _expandedNodes.get(xPath) != null;
out.write("<tr><td colspan=\"");
out.print(level);
out.write("\">");
if (_selectedNodes.get(xPath) != null) {
out.write("<a name=\"");
out.write(xPath);
out.write("\"><img src=\"/icons/ab2_toc_index.gif\" border=0 width=18 height=17>");
out.write("</a>");
}
out.write("</td><td> </td>");
if (node.isLeaf())
out.write("<td></td>");
else {
out.write("<td><a href=\"/servlet/toc?");
out.write(toExpand ? "collapse=" : "expand=");
out.write(xPath);
out.write("\"><img src=\"/icons/ab2_");
out.write(toExpand ? "minus" : "plus");
out.write(".gif\" border=0 width=21 height=17></a></td>");
}
out.write("<td colspan=\"");
out.print(6 - level);
out.write("\" nowrap><a href=\"/servlet/text?fragment=");
out.write(xPath);
out.write("\" target=\"text\">");
out.write(node.getTitle());
out.write("</a></td></tr>");
if (toExpand && !node.isLeaf()) {
final int nChildren = node.getChildCount();
for (int i = 0; i < nChildren; i++)
walkTocTree(level + 1, (TocTree.TocNode)node.getChildAt(i), out);
}
}
public synchronized void retrieveCurrentResults(PrintWriter out) {
if (_currentQueryResults != null && _currentQueryResults.isNonEmpty()) {
QueryHitIterator iter = _currentQueryResults.makeQueryHitIterator();
int resultCounter = 0;
do {
final QueryHitData hit = iter.getHit();
final String doc = hit.getDocument();
final int n = hit.getNumberOfTerms();
out.write("<a href=\"/servlet/document?hit=");
out.print(resultCounter++);
out.write("\" target=\"_top\">");
out.write(doc.substring(doc.lastIndexOf('/') + 1));
out.write("</a> (");
for (int i = 0; i < n; i++) {
String term = hit.getTerm(i);
out.write(term != null ? term : "--");
if (i < n - 1)
out.write(", ");
}
out.write(")<br>");
}
while (iter.next());
}
else
out.write("no results yet");
}
public synchronized QueryResults runQuery(String terms) {
return runQuery(terms, _lastScope);
}
public synchronized QueryResults runQueryNewScope(String scope) {
return runQuery(_lastQuery, scope);
}
public synchronized String getLastQuery() {
return _lastQuery;
}
private QueryResults runQuery(String terms, String scope) {
_lastQuery = terms;
_lastScope = scope;
try {
_tocTreeCache.clear();
_docFragments.clear();
_hitUrlStrings.clear();
if (_queryProcessor != null) {
terms = terms.trim();
if (terms.length() > 0) {
scope = scope.trim();
if (scope.length() == 0)
scope = null;
QueryStatement qs = new QueryStatement(terms, scope, 50);
_currentQueryResults = _queryProcessor.processQuery(qs);
_currentQueryResults.translate();
groupHits(_currentQueryResults);
return _currentQueryResults;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Hashtable _docTypes = new Hashtable();
private void groupHits(QueryResults hits) {
_docRequests.clear();
_docTypes.clear();
if (hits.isNonEmpty()) {
QueryHitIterator iter = hits.makeQueryHitIterator();
do {
QueryHitData hit = iter.getHit();
String doc = hit.getDocument();
Vector docLocators = (Vector)_docRequests.get(doc);
if (docLocators == null) {
_docRequests.put(doc, docLocators = new Vector());
_docTypes.put(doc, hit.getDocumentType());
}
docLocators.addElement(hit.getLocator());
}
while (iter.next());
Enumeration keys = _docRequests.keys();
do {
final String doc = (String)keys.nextElement();
final String docType = (String)_docTypes.get(doc);
DocumentRequest request = new DocumentRequest(doc, docType);
request.setLocators((Vector)_docRequests.get(doc));
_docRequests.put(doc, request);
}
while (keys.hasMoreElements());
}
}
private DocumentFragment getDocumentFragment(DocumentRequest request)
throws Exception {
String document = request.getDocument();
TocTree tocTree = (TocTree)_tocTreeCache.get(document);
final boolean tocNeeded = tocTree == null;
// request TOC if not in cache
request.requestToc(tocNeeded);
DocumentFragment result = _docServer.getDocumentFragment(request);
if (tocNeeded)
_tocTreeCache.put(document, result.getTocTree());
else
result.setTOC(tocTree); // reuse cached TOC
return result;
}
}