http://bugs.eclipse.org/bugs/show_bug.cgi?id=17030 Need to search documentation for exact string match
diff --git a/org.eclipse.help.webapp/build.xml b/org.eclipse.help.webapp/build.xml
index 63d96b9..0e36ebc 100644
--- a/org.eclipse.help.webapp/build.xml
+++ b/org.eclipse.help.webapp/build.xml
@@ -8,7 +8,7 @@
 

 	<target name="init" depends="properties">

 		<property name="plugin" value="org.eclipse.help.webapp"/>

-		<property name="version.suffix" value="_2.0.1"/>

+		<property name="version.suffix" value="_2.0.2"/>

 		<property name="full.name" value="${plugin}${version.suffix}"/>

 		<property name="temp.folder" value="${basedir}/temp.folder"/>

 		<property name="plugin.destination" value="${basedir}"/>

diff --git a/org.eclipse.help.webapp/buildinfocenter.xml b/org.eclipse.help.webapp/buildinfocenter.xml
index 375f4df..9beb6a5 100644
--- a/org.eclipse.help.webapp/buildinfocenter.xml
+++ b/org.eclipse.help.webapp/buildinfocenter.xml
@@ -8,7 +8,7 @@
 
 	<target name="init" depends="properties">
 		<property name="plugin" value="org.eclipse.help.webapp"/>
-		<property name="version.suffix" value="_2.0.1"/>
+		<property name="version.suffix" value="_2.0.2"/>
 		<!-- custom change begin -->
 		<!--
 		<property name="full.name" value="${plugin}${version.suffix}"/>
diff --git a/org.eclipse.help.webapp/doc/infocenter.html b/org.eclipse.help.webapp/doc/infocenter.html
index 1e0e2df..310dd21 100644
--- a/org.eclipse.help.webapp/doc/infocenter.html
+++ b/org.eclipse.help.webapp/doc/infocenter.html
@@ -90,7 +90,7 @@
                                                
       <blockquote>Log into the Administrative Console, expand Nodes -&gt;
 HOSTNAME -&gt; Application Servers -&gt; Default Server -&gt; Process Definition, 
-select "JVM Settings" in the tree. &nbsp;On JVM Settings form, type "<tt>D:\eclipse\plugins\org.eclipse.help.webapp_2.0.1\eclipseurl.jar</tt>"
+select "JVM Settings" in the tree. &nbsp;On JVM Settings form, type "<tt>D:\eclipse\plugins\org.eclipse.help.webapp_2.0.2\eclipseurl.jar</tt>"
 in the Classpath field. &nbsp;Click OK button.</blockquote>
                                                                         
                                              <b>Tomcat</b>              
@@ -100,7 +100,7 @@
         line:<br>
                          <tt>set CLASSPATH=%JAVA_HOME%\lib\tools.jar</tt><br>
    insert the line:<br>
-           <tt>set CLASSPATH=%CLASSPATH%;D:\eclipse\plugins\org.eclipse.help.webapp_2.0.1\eclipseurl.jar<br>
+           <tt>set CLASSPATH=%CLASSPATH%;D:\eclipse\plugins\org.eclipse.help.webapp_2.0.2\eclipseurl.jar<br>
           <br>
           </tt>                                       </blockquote>
          </td>
diff --git a/org.eclipse.help.webapp/plugin.xml b/org.eclipse.help.webapp/plugin.xml
index 0a42463..75d79eb 100644
--- a/org.eclipse.help.webapp/plugin.xml
+++ b/org.eclipse.help.webapp/plugin.xml
@@ -9,7 +9,7 @@
 <plugin

    name = "%help_plugin_name"

    id = "org.eclipse.help.webapp"

-   version = "2.0.1"

+   version = "2.0.2"

    provider-name = "%providerName">

 

    <requires>

diff --git a/org.eclipse.help.webapp/src/org/eclipse/help/servlet/EclipseConnector.java b/org.eclipse.help.webapp/src/org/eclipse/help/servlet/EclipseConnector.java
index 619903f..eb993fe 100644
--- a/org.eclipse.help.webapp/src/org/eclipse/help/servlet/EclipseConnector.java
+++ b/org.eclipse.help.webapp/src/org/eclipse/help/servlet/EclipseConnector.java
@@ -200,8 +200,7 @@
 				if (UrlUtil.getRequestParameter(req, "resultof") != null)

 					return new IFilter[] {

 						cssFilter,

-						new HighlightFilter(

-							UrlUtil.getRequestParameter(req, "resultof"))};

+						new HighlightFilter(req)};

 				else

 					return basicFilters;

 			} else

@@ -209,8 +208,7 @@
 		} else {

 			if (UrlUtil.getRequestParameter(req, "resultof") != null)

 				return new IFilter[] {

-					new HighlightFilter(

-						UrlUtil.getRequestParameter(req, "resultof"))};

+					new HighlightFilter(req)};

 			else

 				return noFilters;

 		}

diff --git a/org.eclipse.help.webapp/src/org/eclipse/help/servlet/HighlightFilter.java b/org.eclipse.help.webapp/src/org/eclipse/help/servlet/HighlightFilter.java
index c04abd9..e9cf5a2 100644
--- a/org.eclipse.help.webapp/src/org/eclipse/help/servlet/HighlightFilter.java
+++ b/org.eclipse.help.webapp/src/org/eclipse/help/servlet/HighlightFilter.java
@@ -7,19 +7,24 @@
 

 import java.util.*;

 

+import javax.servlet.http.HttpServletRequest;

+

 public class HighlightFilter implements IFilter {

 	private String searchWord;

+	private HttpServletRequest request;

 

 	private static final String scriptPart1 =

 		"\n<script language=\"JavaScript\">\n<!--\nvar keywords = new Array (";

 	private static final String scriptPart3 =

-		");\nvar highlighted=false;\nonload=highlight;\ndocument.onreadystatechange=highlight;\nfunction highlight()\n{\nif(highlighted){\nreturn;\n}\nhighlighted=true;\nif (!document.body) return; var newText = document.body.createTextRange();\nfor (var i = 0; i < keywords.length; i++) {\nwhile (newText.findText(keywords[i]) )\n{\nvar replacement = newText.htmlText\nnewText.pasteHTML(\"<span class=highlight style='background-color:ButtonFace;color:ButtonText;'>\" + replacement + \"</span>\");\n}\nnewText = document.body.createTextRange();\n}\n}\n// -->\n</script>\n";

+		");\n-->\n</script>\n<script language=\"JavaScript\" src=\"";

+	private static final String scriptPart5 = "highlight.js\"></script>\n";

 

 	/**

 	 * Constructor.

 	 */

-	public HighlightFilter(String searchWord) {

-		this.searchWord = searchWord;

+	public HighlightFilter(HttpServletRequest request) {

+		this.request = request;

+		this.searchWord = UrlUtil.getRequestParameter(request, "resultof");

 	}

 

 	/*

@@ -44,6 +49,7 @@
 	 */

 	private byte[] createJScript(Collection keywords) {

 		StringBuffer buf = new StringBuffer(scriptPart1);

+		// append comma separated list of keywords

 		Iterator it = keywords.iterator();

 		if (!it.hasNext())

 			return null;

@@ -53,18 +59,32 @@
 			keyword = (String) it.next();

 			buf.append(", \"").append(keyword).append("\"");

 		}

+		//

 		buf.append(scriptPart3);

+		// append "../" to get to the webapp

+		String path = request.getPathInfo();

+		if (path != null) {

+			for (int i;

+				0 <= (i = path.indexOf('/'));

+				path = path.substring(i + 1)) {

+				buf.append("../");

+			}

+		}

+		//

+		buf.append(scriptPart5);

 		return buf.toString().getBytes();

 	}

 	/**

-	 * Extracts keywords from query 

+	 * Extracts keywords from query that contains

+	 * keywords dobule quoted and separated by space

 	 * @return Collection of String

 	 */

 	private Collection getWords() {

-		Collection tokens = new ArrayList();

-		//Divide along quotation marks and brackets

+		// Collect words to hash set to eliminate duplcates

+		Collection tokens = new HashSet();

+		//Divide along quotation marks

 		StringTokenizer qTokenizer =

-			new StringTokenizer(searchWord.trim(), "\"()", true);

+			new StringTokenizer(searchWord.trim(), "\"", true);

 		boolean withinQuotation = false;

 		String quotedString = "";

 		while (qTokenizer.hasMoreTokens()) {

@@ -81,28 +101,11 @@
 				continue;

 			}

 			if (withinQuotation) {

-				quotedString += (curToken);

-			} else {

-				//divide not quoted strings along white space

-				StringTokenizer parser = new StringTokenizer(curToken.trim());

-				while (parser.hasMoreTokens()) {

-					tokens.add(parser.nextToken());

-				}

+				tokens.add(curToken);

 			}

-

 		}

 

-		Collection words = new HashSet(); // to eliminate duplicate words

-		for (Iterator it = tokens.iterator(); it.hasNext();) {

-			String token = (String) it.next();

-			String tokenLowerCase = token.toLowerCase(Locale.US);

-			if (!tokenLowerCase.equals("\"")

-				&& !tokenLowerCase.equals("and")

-				&& !tokenLowerCase.equals("or")

-				&& !tokenLowerCase.equals("not"))

-				words.add(token);

-		}

-		return words;

+		return tokens;

 

 	}

 	/**

diff --git a/org.eclipse.help/plugin.xml b/org.eclipse.help/plugin.xml
index a2c8a2d..e7dee48 100644
--- a/org.eclipse.help/plugin.xml
+++ b/org.eclipse.help/plugin.xml
@@ -7,7 +7,7 @@
 <plugin

 	name = "%help_plugin_name"

 	id = "org.eclipse.help"

-	version = "2.0.1"

+	version = "2.0.2"

 	provider-name = "%providerName"

 	class="org.eclipse.help.internal.HelpPlugin">

 

diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java b/org.eclipse.help/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java
index ce06e12..c32fab6 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java
@@ -46,14 +46,14 @@
 			this.luceneAnalyzer = new DefaultAnalyzer(locale);
 			this.lang = locale;
 		}
-
+		
 	}
 	/**
 	 * Gets the analyzer.
 	 * @return Returns a Analyzer
 	 */
 	public Analyzer getAnalyzer() {
-		return luceneAnalyzer;
+		return new SmartAnalyzer(lang, luceneAnalyzer);
 	}
 
 	/**
diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/IndexingOperation.java b/org.eclipse.help/src/org/eclipse/help/internal/search/IndexingOperation.java
index 944a19a..86fe29c 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/IndexingOperation.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/IndexingOperation.java
@@ -84,8 +84,10 @@
 		// if collection is empty, we may return right away

 		// need to check if we have to do anything to the progress monitor

 		int numDocs = removedDocs.size() + addedDocs.size();

-		if (numDocs <= 0)

+		if (numDocs <= 0){

+			pm.done();

 			return;

+		}

 		int workTotal = WORK_PREPARE + numDocs * WORK_INDEXDOC + WORK_SAVEINDEX;

 		pm.beginTask("" /*Resources.getString("Index_needs_updated")*/

 		, workTotal);

diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/QueryBuilder.java b/org.eclipse.help/src/org/eclipse/help/internal/search/QueryBuilder.java
index f70b020..8c34c93 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/QueryBuilder.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/QueryBuilder.java
@@ -92,7 +92,7 @@
 					if (!highlightWords.contains(token.value))

 						highlightWords.add(token.value);

 				} else {

-					List wordList = analyzeText(analyzer, token.value);

+					List wordList = analyzeText(analyzer, "contents", token.value);

 					

 					// add original word to the list of words to highlight

 					if (wordList.size() > 0 && !highlightWords.contains(token.value))

@@ -115,7 +115,7 @@
 				newTokens.add(token);

 			else if (token.type == QueryWordsToken.PHRASE) {

 				QueryWordsPhrase phrase = QueryWordsToken.phrase();

-				List wordList = analyzeText(analyzer, token.value);

+				List wordList = analyzeText(analyzer, "exact_contents", token.value);

 

 				// add original word to the list of words to highlight

 				if (wordList.size() > 0 && !highlightWords.contains(token.value))

@@ -126,8 +126,8 @@
 					phrase.addWord(word);

 					

 					// add analyzed word to the list of words to highlight

-					if (!highlightWords.contains(word))

-						highlightWords.add(word);

+					// if (!highlightWords.contains(word))

+					//	highlightWords.add(word);

 				}

 				// add phrase only if not empty

 				if (phrase.getWords().size() > 0) {

@@ -141,10 +141,10 @@
 	 * Get a list of tokens corresponding to a search word or phrase

 	 * @return List of String

 	 */

-	private List analyzeText(Analyzer analyzer, String text) {

+	private List analyzeText(Analyzer analyzer, String fieldName, String text) {

 		List words = new ArrayList(1);

 		Reader reader = new StringReader(text);

-		TokenStream tStream = analyzer.tokenStream("contents", reader);

+		TokenStream tStream = analyzer.tokenStream(fieldName, reader);

 		Token tok;

 		try {

 			while (null != (tok = tStream.next())) {

@@ -330,16 +330,17 @@
 		return booleanQuery;

 	}

 	/**

-	 * Obtains analyzed words from query as one string.

-	 * Words are separated by space.

+	 * Obtains analyzed terms from query as one string.

+	 * Words are double quoted, and separated by space.

 	 * The analyzed words are needed for highlighting

 	 * word roots.

 	 */

-	public String getAnalyzedWords() {

+	public String gethighlightTerms() {

 		StringBuffer buf = new StringBuffer();

 		for (Iterator it = highlightWords.iterator(); it.hasNext();) {

+			buf.append('"');

 			buf.append(it.next());

-			buf.append(' ');

+			buf.append("\" ");

 		}

 

 		return buf.toString();

diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsPhrase.java b/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsPhrase.java
index 4e4e42c..2e11a4d 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsPhrase.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsPhrase.java
@@ -34,7 +34,7 @@
 		for (Iterator it = getWords().iterator(); it.hasNext();)

 		{

 			String word = (String) it.next();

-			Term t = new Term(field, word);

+			Term t = new Term("exact_"+field, word);

 			q.add(t);

 			q.setBoost(boost);

 		}

diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsToken.java b/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsToken.java
index 13c2f59..9957108 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsToken.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/QueryWordsToken.java
@@ -28,12 +28,13 @@
 	 * Creates a lucene query for a field

 	 */

 	public Query createLuceneQuery(String field, float boost) {

-		Term t = new Term(field, value);

 		Query q;

 		if (value.indexOf('?') >= 0 || value.indexOf('*') >= 0) {

+			Term t = new Term("exact_"+field, value);

 			q = new WildcardQuery(t);

 			((WildcardQuery) q).setBoost(boost);

 		} else {

+			Term t = new Term(field, value);

 			q = new TermQuery(t);

 			((TermQuery) q).setBoost(boost);

 		}

diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/SearchIndex.java b/org.eclipse.help/src/org/eclipse/help/internal/search/SearchIndex.java
index 9e0a2f4..9390c84 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/SearchIndex.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/SearchIndex.java
@@ -67,13 +67,18 @@
 			Document doc = new Document();

 			doc.add(Field.Keyword("name", name));

 			HTMLParser parser = new HTMLParser(stream);

-			doc.add(Field.Text("contents", parser.getReader()));

+			

+			ParsedDocument parsed=new ParsedDocument(parser.getReader());

+			

+			doc.add(Field.Text("contents", parsed.newContentReader()));

+			doc.add(Field.Text("exact_contents", parsed.newContentReader()));

 			String title = "";

 			try {

 				title = parser.getTitle();

 			} catch (InterruptedException ie) {

 			}

 			doc.add(Field.UnStored("title", title));

+			doc.add(Field.UnStored("exact_title", title));

 			doc.add(Field.UnIndexed("raw_title", title));

 			// doc.add(Field.UnIndexed("summary", parser.getSummary()));

 			iw.addDocument(doc);

@@ -219,11 +224,11 @@
 				queryBuilder.getLuceneQuery(

 					searchQuery.getFieldNames(),

 					searchQuery.isFieldSearch());

-			String analyzedWords = queryBuilder.getAnalyzedWords();

+			String highlightTerms = queryBuilder.gethighlightTerms();

 			if (luceneQuery != null) {

 				Searcher searcher = new IndexSearcher(indexDir.getAbsolutePath());

 				Hits hits = searcher.search(luceneQuery);

-				collector.addHits(hits, analyzedWords);

+				collector.addHits(hits, highlightTerms);

 				searcher.close();

 			}

 		} catch (Exception e) {

diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/SearchResults.java b/org.eclipse.help/src/org/eclipse/help/internal/search/SearchResults.java
index 89ad50b..0c3251f 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/SearchResults.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/SearchResults.java
@@ -34,8 +34,8 @@
 	 * Adds hits to the result
 	 * @param Hits hits
 	 */
-	public void addHits(Hits hits, String analyzedWords) {
-		String urlEncodedWords = URLCoder.encode(analyzedWords);
+	public void addHits(Hits hits, String highlightTerms) {
+		String urlEncodedWords = URLCoder.encode(highlightTerms);
 		List searchHitList = new ArrayList();
 		float scoreScale = 1.0f;
 		boolean scoreScaleSet = false;
diff --git a/org.eclipse.help/src/org/eclipse/help/internal/search/WordTokenStream.java b/org.eclipse.help/src/org/eclipse/help/internal/search/WordTokenStream.java
index 642ec62..e43ec5e 100644
--- a/org.eclipse.help/src/org/eclipse/help/internal/search/WordTokenStream.java
+++ b/org.eclipse.help/src/org/eclipse/help/internal/search/WordTokenStream.java
@@ -76,10 +76,10 @@
 			int end = boundary.next();
 				end != BreakIterator.DONE;
 				start = end, end = boundary.next()) {
-			// determine if it is a word or characters between
-			// any letter between boundaries means it is a word
+			// determine if it is a word
+			// any letter or digit between boundaries means it is a word
 			for (int i = start; i < end; i++) {
-				if (Character.isLetter(str.charAt(i))) {
+				if (Character.isLetterOrDigit(str.charAt(i))) {
 					// it is a word
 					tokenList.add(new Token(str.substring(start, end), start, end));
 					continue wordsbreak;