Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonrad Kolosowski2003-08-25 15:37:14 -0400
committerKonrad Kolosowski2003-08-25 15:37:14 -0400
commitc280e9ffc6e8a427789a90fe2fcf13741e3a4a44 (patch)
treec57cf9765c09a6f1f60f75ae7d11aca2b586ccd6
parent0fec3f388e537182b1c92a0534bb042e1a3356b1 (diff)
downloadeclipse.platform.ua-c280e9ffc6e8a427789a90fe2fcf13741e3a4a44.tar.gz
eclipse.platform.ua-c280e9ffc6e8a427789a90fe2fcf13741e3a4a44.tar.xz
eclipse.platform.ua-c280e9ffc6e8a427789a90fe2fcf13741e3a4a44.zip
RCP work 1
-rw-r--r--org.eclipse.help.base/.options2
-rw-r--r--org.eclipse.help.base/doc/banner_prod.jpgbin0 -> 11300 bytes
-rw-r--r--org.eclipse.help.base/doc/book.css1
-rw-r--r--org.eclipse.help.base/doc/contents_view.gifbin0 -> 169 bytes
-rw-r--r--org.eclipse.help.base/doc/help_home.html51
-rw-r--r--org.eclipse.help.base/doc/org_eclipse_help_base.html78
-rw-r--r--org.eclipse.help.base/doc/search_results_view.gifbin0 -> 162 bytes
-rw-r--r--org.eclipse.help.base/doc/synch_toc_nav.gifbin0 -> 361 bytes
-rw-r--r--org.eclipse.help.base/plugin.properties29
-rw-r--r--org.eclipse.help.base/plugin.xml212
-rw-r--r--org.eclipse.help.base/preferences.ini47
-rw-r--r--org.eclipse.help.base/schema/browser.exsd166
-rw-r--r--org.eclipse.help.base/schema/luceneAnalyzer.exsd133
-rw-r--r--org.eclipse.help.base/schema/webapp.exsd110
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/browser/IBrowser.java69
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/browser/IBrowserFactory.java31
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/browser/package.html40
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/DefaultHelpSupport.java276
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/HelpApplication.java109
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/HelpBasePlugin.java158
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/HelpSystem.java321
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/IndexToolApplication.java161
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserDescriptor.java38
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserLog.java88
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserManager.java288
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/CurrentBrowser.java135
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowser.java154
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowserFactory.java34
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaBrowserAdapter.java232
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaFactory.java123
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/StreamConsumer.java47
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserAdapter.java85
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserFactory.java39
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/ASCIIReader.java50
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java164
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/Analyzer_en.java74
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/DefaultAnalyzer.java105
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/HTMLDocParser.java381
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchHitCollector.java24
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchQuery.java32
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/IndexingOperation.java294
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/LazyProgressMonitor.java57
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/ParsedDocument.java73
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/ProgressDistributor.java132
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryBuilder.java411
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsExactPhrase.java50
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsPhrase.java51
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsToken.java73
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchHit.java79
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java487
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchManager.java167
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchProgressMonitor.java211
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchQuery.java102
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchResults.java169
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/SmartAnalyzer.java45
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/search/WordTokenStream.java101
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Eclipse.java212
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseConnection.java152
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseController.java219
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Options.java307
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneHelp.java180
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneInfocenter.java100
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/util/PluginVersionInfo.java190
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableHelpResource.java103
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableToc.java70
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTocsArray.java104
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTopic.java101
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/IHelpWorkingSetManager.java69
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/PropertyChange.java306
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSet.java84
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetComparator.java43
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetManager.java479
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/standalone/Help.java159
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/standalone/Infocenter.java101
-rw-r--r--org.eclipse.help.base/src/org/eclipse/help/standalone/package.html27
75 files changed, 9595 insertions, 0 deletions
diff --git a/org.eclipse.help.base/.options b/org.eclipse.help.base/.options
new file mode 100644
index 000000000..d9a40a5d2
--- /dev/null
+++ b/org.eclipse.help.base/.options
@@ -0,0 +1,2 @@
+org.eclipse.help/debug = true
+org.eclipse.help/debug/search = false
diff --git a/org.eclipse.help.base/doc/banner_prod.jpg b/org.eclipse.help.base/doc/banner_prod.jpg
new file mode 100644
index 000000000..667cd67bf
--- /dev/null
+++ b/org.eclipse.help.base/doc/banner_prod.jpg
Binary files differ
diff --git a/org.eclipse.help.base/doc/book.css b/org.eclipse.help.base/doc/book.css
new file mode 100644
index 000000000..139eb28a0
--- /dev/null
+++ b/org.eclipse.help.base/doc/book.css
@@ -0,0 +1 @@
+P.Code { display: block; text-align: left; text-indent: 0.00pt; margin-top: 0.000000pt; margin-bottom: 0.000000pt; margin-right: 0.000000pt; margin-left: 15pt; font-size: 10.000000pt; font-weight: medium; font-style: Regular; color: #4444CC; text-decoration: none; vertical-align: baseline; text-transform: none; font-family: "Courier New"; } H6.CaptionFigColumn { display: block; text-align: left; text-indent: 0.000000pt; margin-top: 3.000000pt; margin-bottom: 11.000000pt; margin-right: 0.000000pt; margin-left: 0.000000pt; font-size: 9.000000pt; font-weight: medium; font-style: Italic; color: #000000; text-decoration: none; vertical-align: baseline; text-transform: none; font-family: "Arial"; } P.Note { display: block; text-align: left; text-indent: 0pt; margin-top: 19.500000pt; margin-bottom: 19.500000pt; margin-right: 0.000000pt; margin-left: 30pt; font-size: 11.000000pt; font-weight: medium; font-style: Italic; color: #000000; text-decoration: none; vertical-align: baseline; text-transform: none; font-family: "Arial"; } EM.UILabel { font-weight: Bold; font-style: Regular; text-decoration: none; vertical-align: baseline; text-transform: none; } EM.CodeName { font-weight: Bold; font-style: Regular; text-decoration: none; vertical-align: baseline; text-transform: none; font-family:"Courier New"; } /* following font face declarations need to be removed for DBCS */ body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt {font-family: Arial, Helvetica, sans-serif; color: #000000} pre { font-family: Courier, monospace} /* end font face declarations */ /* following font size declarations should be OK for DBCS */ body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt {font-size: 10pt; } pre { font-size: 10pt} /* end font size declarations */ body { background: #FFFFFF} h1 { font-size: 18pt; margin-top: 5; margin-bottom: 1 } h2 { font-size: 14pt; margin-top: 25; margin-bottom: 3 } h3 { font-size: 11pt; margin-top: 20; margin-bottom: 3 } h4 { font-size: 10pt; margin-top: 20; margin-bottom: 3; font-style: italic } p { margin-top: 10px; margin-bottom: 10px } pre { margin-left: 6; font-size: 9pt } a:link { color: #0000FF } a:hover { color: #000080 } a:visited { text-decoration: underline } ul { margin-top: 0; margin-bottom: 10 } li { margin-top: 0; margin-bottom: 0 } li p { margin-top: 0; margin-bottom: 0 } ol { margin-top: 0; margin-bottom: 10 } dl { margin-top: 0; margin-bottom: 10 } dt { margin-top: 0; margin-bottom: 0; font-weight: bold } dd { margin-top: 0; margin-bottom: 0 } strong { font-weight: bold} em { font-style: italic} var { font-style: italic} div.revision { border-left-style: solid; border-left-width: thin; border-left-color: #7B68EE; padding-left:5 } th { font-weight: bold } \ No newline at end of file
diff --git a/org.eclipse.help.base/doc/contents_view.gif b/org.eclipse.help.base/doc/contents_view.gif
new file mode 100644
index 000000000..20e0ff988
--- /dev/null
+++ b/org.eclipse.help.base/doc/contents_view.gif
Binary files differ
diff --git a/org.eclipse.help.base/doc/help_home.html b/org.eclipse.help.base/doc/help_home.html
new file mode 100644
index 000000000..3f1c1f465
--- /dev/null
+++ b/org.eclipse.help.base/doc/help_home.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+
+<head>
+<meta http-equiv="Content-Language" content="en-us">
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>Using Eclipse help system</title>
+<LINK REL="STYLESHEET" HREF="book.css" CHARSET="ISO-8859-1" TYPE="text/css">
+</head>
+
+<body style="margin-top: 0; margin-left: 0; margin-right: 0;">
+<table height=45 width="100%" cellspacing="0" cellpadding="0" border="0" style="background:black;">
+ <tr><td align=left valign="center"><img src="banner_prod.jpg" alt="Eclipse icon"></td></tr>
+</table>
+
+<table width="100%" cellspacing="0" cellpadding="10" border="0">
+<tr><td>
+<h2>Using Eclipse help system</h2>
+<p>Browse topics in the <b>Contents</b> frame
+ (<img src="contents_view.gif" width="16" height="16" alt="Contents icon">)
+on the left. Click on topics to
+ display them. Use the <b>Back</b> and <b>Forward</b> buttons
+ to navigate within history of viewed topics.</p>
+
+<h3>Searching</h3>
+<p>To quickly locate topics on particular subject in the entire documentation set,
+ enter a query in the <b>Search</b> field. Click
+ (<img src="search_results_view.gif" width="16" height="16" alt="Search icon">)
+ icon to display Search view. You can narrow <b>scope</b> of the search by selecting
+ only sections that you are interested in.</p>
+
+<h3>Synchronizing</h3>
+<p>After you run a search and find a topic you were looking for, click the <b>
+ Synchronize</b> button (<img src="synch_toc_nav.gif" width="16" height="16" alt="Synchronize icon">)
+ to match the navigation tree up with the current topic. You might also find
+ it useful to synchronize after following in-topic links.</p>
+
+<h3>More information</h3>
+<p>If you cannot find solution to your question in the on-line help,
+ visit <b><a href="http://eclipse.org/" target="_new">eclipse.org</a></b>
+ web site, read articles, and participate in the eclipse community.</p>
+
+<p><br>
+<a href="hglegal2003.htm"><img src="ngibmcpy2003.gif" alt=
+"(c) Copyright (c) 2000, 2003 IBM Corporation and others. All Rights Reserved." border="0"></a>
+</p>
+
+</td></tr></table>
+</body>
+
+</html> \ No newline at end of file
diff --git a/org.eclipse.help.base/doc/org_eclipse_help_base.html b/org.eclipse.help.base/doc/org_eclipse_help_base.html
new file mode 100644
index 000000000..19d4fa6bd
--- /dev/null
+++ b/org.eclipse.help.base/doc/org_eclipse_help_base.html
@@ -0,0 +1,78 @@
+<!DOCTYPE doctype PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=iso-8859-1">
+
+ <meta name="GENERATOR"
+ content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
+ <title>Help System Base Extension Points</title>
+</head>
+<body text="#000000" bgcolor="#ffffff" link="#0000ff" vlink="#800080"
+ alink="#ff0000">
+<center>
+<h1>Help System Base Preferences</h1>
+</center>
+
+The following preferences settings can be used with the help system:
+<ul>
+ <li><b>help_home</b>: The page to show inn the content area when opening
+help. Specify your html page as <i>/pluginId/path/to/home.html</i>. Currently,
+the default home is /org.eclipse.help/doc/help_home.html.</li>
+ <li><b>linksView</b>: set to true or false to control the visibility of
+the related links view. Note: this option has no effect in the infocenter.</li>
+ <li><b>bookmarksView</b>: set to true or false to control the visibility
+of the bookmarks view. Note: this option has no effect in the infocenter.</li>
+ <li><b>windowTitlePrefix</b>: set to true or false to control the title
+of the browser window. If true, the title will have a form "Help -
+&lt;PRODUCT_NAME&gt;", otherwise the title will be "&lt;PRODDUCT_NAME&gt;",
+where &lt;PRODUCT_NAME&gt; is the name of Eclipse product set in the primary
+feature. Default is true.</li>
+ <li><b>loadBookAtOnceLimit</b>: the maximum number of topics
+a book can have, for the navigation to be loaded by the browser as one document.
+Navigation for larger books is loaded dynamically, few levels at a time.
+More topics are downloaded as necessary, when branches are expanded. Default is 1000</li>
+ <li><b>dynamicLoadDepthsHint</b>: suggested number of levels in topic navigation
+downloaded to the browser for large books. The value needs to be greater than 0.
+The actual number of levels can differ for wide tree if suggested number of levels
+contains large number of topics. Default is 3.</li>
+ <li><b>imagesDirectory</b>: directory containing images used in the help
+view. Images must have the same name as those in the org.eclipse.help.webapp
+plugin. Use the /pluginID/directory format. Default is "images".</li>
+ <li><b>advanced.toolbarBackground</b>: CSS background for toolbars.&nbsp;
+Value is used in browsers that display advanced help UI.&nbsp; Default is
+"ButtonFace"</li>
+ <li><b>advanced.viewBackground</b>: CSS background for navigation views.&nbsp;
+Value is used in browsers that display advanced help UI.&nbsp; Default is
+"Window"</li>
+ <li><b>advanced.toolbarFont</b>: CSS font for toolbars.&nbsp; Value is used
+in browsers that display advanced help UI.&nbsp; Default is "icon"</li>
+ <li><b>advanced.viewFont</b>: CSS font for navigation views.&nbsp; Value
+is used in browsers that display advanced help UI.&nbsp; Default is "icon"</li>
+ <li><b>basic.toolbarBackground</b>: background color for toolbars.&nbsp;
+Value is used in browsers displaying basic help UI.&nbsp; Default is "#D4D0C8"</li>
+ <li><b>basic.viewBackground</b>: background color for navigation views.&nbsp;
+Value is used in browsers displaying basic help UI.&nbsp; Default is "#FFFFFF"</li>
+ <li><b>productIndex</b>: plug-in ID of a plugin containing prebuilt documentation
+index. &nbsp;If present, the index retrieved from the plug-in will be used
+as a starting point in creation of documentation index for use by help search.&nbsp;
+Default is ""<br></li>
+ <li><b>locales</b>: list of locales (space separated) that infocenter will recognize and provide a customized
+content for. If locales (or languages) accepted by client browser are not matched with
+any locales in this list, the browser will be served content for default locale - the server locale,
+or locale specified by eclipse -nl command line option. If list is not specified, the browser will be
+served contents for its preferred locale. Note: not providing this option may result in a large
+memory and disk space requirements as navigations and indexes will be created for each distinct
+preferred locale among browsers accessing the infocenter.
+Thos preference can be overridden by command line option when launching infocenter.
+Default is ""<br></li>
+</ul>
+<p><br>
+<a href="hglegal2003.htm"><img src="ngibmcpy2003.gif"
+ alt="(c) Copyright (c) 2000, 2003 IBM Corporation and others. All Rights Reserved."
+ border="0"></a></p>
+<br>
+<br>
+</body>
+</html>
diff --git a/org.eclipse.help.base/doc/search_results_view.gif b/org.eclipse.help.base/doc/search_results_view.gif
new file mode 100644
index 000000000..6f9036cb1
--- /dev/null
+++ b/org.eclipse.help.base/doc/search_results_view.gif
Binary files differ
diff --git a/org.eclipse.help.base/doc/synch_toc_nav.gif b/org.eclipse.help.base/doc/synch_toc_nav.gif
new file mode 100644
index 000000000..2bc7cab3b
--- /dev/null
+++ b/org.eclipse.help.base/doc/synch_toc_nav.gif
Binary files differ
diff --git a/org.eclipse.help.base/plugin.properties b/org.eclipse.help.base/plugin.properties
new file mode 100644
index 000000000..a16b9a3ad
--- /dev/null
+++ b/org.eclipse.help.base/plugin.properties
@@ -0,0 +1,29 @@
+###############################################################################
+# Copyright (c) 2000, 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+help_base_plugin_name = Help System Base
+providerName=Eclipse.org
+support_extension_point_name = Pluggable Help Support
+toc_extension_point_name = Help Table of Contents Contributions
+webapp_extension_point_name = Help Web Application
+contributions_extension_point_name = Help Contributions
+contexts_extension_point_name = Context Help
+content_producer_extension_point_name = Help Content Producer
+app_server = Application Server
+prebuilt_index = Prebuilt Index
+lucene_analyzer = Lucene Text Analyzer
+browser_extension_point_name = Web Browser
+mozilla = Mozilla
+mozilla_adapter = Mozilla Adapter
+netscape = Netscape
+netscape_adapter = Netscape Adapter
+custom_browser = Custom Browser (user defined program)
+simple_browser = Simple Browser Adapter
+defaultBrowser = Default Web Browser (Change in System Preferences)
diff --git a/org.eclipse.help.base/plugin.xml b/org.eclipse.help.base/plugin.xml
new file mode 100644
index 000000000..fd3ad7b54
--- /dev/null
+++ b/org.eclipse.help.base/plugin.xml
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ id="org.eclipse.help.base"
+ name="%help_base_plugin_name"
+ version="3.0.0"
+ provider-name="%providerName"
+ class="org.eclipse.help.internal.HelpBasePlugin">
+
+ <runtime>
+ <library name="helpbase.jar">
+ <export name="*"/>
+ <packages prefixes="org.eclipse.help"/>
+ </library>
+ </runtime>
+ <requires>
+ <import plugin="org.apache.lucene"/>
+ <import plugin="org.apache.xerces"/>
+ <import plugin="org.eclipse.help"/>
+ <import plugin="org.eclipse.help.appserver"/>
+ </requires>
+
+
+<!-- Extension points -->
+ <extension-point id="luceneAnalyzer" name="%lucene_analyzer" schema="schema/luceneAnalyzer.exsd"/>
+ <extension-point id="webapp" name="%webapp_extension_point_name" schema="schema/webapp.exsd"/>
+ <extension-point id="browser" name="%browser_extension_point_name" schema="schema/browser.exsd"/>
+
+<!-- Stand-alone infocenter application -->
+ <extension
+ id="infocenterApplication"
+ point="org.eclipse.core.runtime.applications">
+ <application>
+ <run
+ class="org.eclipse.help.internal.HelpApplication">
+ <parameter
+ name="mode"
+ value="infocenter">
+ </parameter>
+ </run>
+ </application>
+ </extension>
+<!-- Stand-alone help application -->
+ <extension
+ id="helpApplication"
+ point="org.eclipse.core.runtime.applications">
+ <application>
+ <run
+ class="org.eclipse.help.internal.HelpApplication">
+ <parameter
+ name="mode"
+ value="standalone">
+ </parameter>
+ </run>
+ </application>
+ </extension>
+<!-- Pre-indexing tool -->
+ <extension
+ id="indexTool"
+ point="org.eclipse.core.runtime.applications">
+ <application>
+ <run
+ class="org.eclipse.help.internal.IndexToolApplication">
+ </run>
+ </application>
+ </extension>
+<!-- Text Analyzers for search -->
+ <extension
+ id="org.eclipse.help.Analyzer_en"
+ point="org.eclipse.help.luceneAnalyzer">
+ <analyzer
+ locale="en"
+ class="org.eclipse.help.internal.search.Analyzer_en">
+ </analyzer>
+ </extension>
+ <extension
+ id="org.eclipse.help.Analyzer_de"
+ point="org.eclipse.help.luceneAnalyzer">
+ <analyzer
+ locale="de"
+ class="org.apache.lucene.analysis.de.GermanAnalyzer">
+ </analyzer>
+ </extension>
+<!-- Web Browsers -->
+ <extension
+ point="org.eclipse.help.browser">
+ <browser
+ name="%mozilla_adapter"
+ id="org.eclipse.help.mozillaLinux">
+ <factoryclass
+ class="org.eclipse.help.internal.browser.MozillaFactory">
+ <parameter
+ name="executable"
+ value="mozilla">
+ </parameter>
+ <parameter
+ name="executableName"
+ value="%mozilla">
+ </parameter>
+ <parameter
+ name="os"
+ value="Linux">
+ </parameter>
+ </factoryclass>
+ </browser>
+ <browser
+ name="%netscape_adapter"
+ id="org.eclipse.help.netscapeLinux">
+ <factoryclass
+ class="org.eclipse.help.internal.browser.MozillaFactory">
+ <parameter
+ name="executable"
+ value="netscape">
+ </parameter>
+ <parameter
+ name="executableName"
+ value="%netscape">
+ </parameter>
+ <parameter
+ name="os"
+ value="Linux">
+ </parameter>
+ </factoryclass>
+ </browser>
+ <browser
+ name="%netscape_adapter"
+ id="org.eclipse.help.netscapeAIX">
+ <factoryclass
+ class="org.eclipse.help.internal.browser.MozillaFactory">
+ <parameter
+ name="executable"
+ value="netscape">
+ </parameter>
+ <parameter
+ name="executableName"
+ value="%netscape">
+ </parameter>
+ <parameter
+ name="os"
+ value="AIX">
+ </parameter>
+ </factoryclass>
+ </browser>
+ <browser
+ name="%mozilla_adapter"
+ id="org.eclipse.help.mozillaHPUX">
+ <factoryclass
+ class="org.eclipse.help.internal.browser.MozillaFactory">
+ <parameter
+ name="executable"
+ value="mozilla">
+ </parameter>
+ <parameter
+ name="executableName"
+ value="%mozilla">
+ </parameter>
+ <parameter
+ name="os"
+ value="HP">
+ </parameter>
+ </factoryclass>
+ </browser>
+ <browser
+ name="%netscape_adapter"
+ id="org.eclipse.help.netscapeHPUX">
+ <factoryclass
+ class="org.eclipse.help.internal.browser.MozillaFactory">
+ <parameter
+ name="executable"
+ value="netscape">
+ </parameter>
+ <parameter
+ name="executableName"
+ value="%netscape">
+ </parameter>
+ <parameter
+ name="os"
+ value="HP">
+ </parameter>
+ </factoryclass>
+ </browser>
+ <browser
+ name="%netscape_adapter"
+ id="org.eclipse.help.netscapeSolaris">
+ <factoryclass
+ class="org.eclipse.help.internal.browser.MozillaFactory">
+ <parameter
+ name="executable"
+ value="netscape">
+ </parameter>
+ <parameter
+ name="executableName"
+ value="%netscape">
+ </parameter>
+ <parameter
+ name="os"
+ value="SunOS">
+ </parameter>
+ </factoryclass>
+ </browser>
+ <browser
+ factoryclass="org.eclipse.help.internal.browser.macosx.DefaultBrowserFactory"
+ name="%defaultBrowser"
+ id="org.eclipse.help.defaultBrowserMacOSX">
+ </browser>
+ <browser
+ factoryclass="org.eclipse.help.internal.browser.CustomBrowserFactory"
+ name="%custom_browser"
+ id="org.eclipse.help.custombrowser">
+ </browser>
+ </extension>
+
+</plugin>
diff --git a/org.eclipse.help.base/preferences.ini b/org.eclipse.help.base/preferences.ini
new file mode 100644
index 000000000..413b81a7c
--- /dev/null
+++ b/org.eclipse.help.base/preferences.ini
@@ -0,0 +1,47 @@
+#banner=/org.eclipse.help.webapp/advanced/banner.html
+#banner_height=45
+help_home=/org.eclipse.help/doc/help_home.html
+linksView=true
+bookmarksView=true
+windowTitlePrefix=true
+
+########################################
+# Customization for handling large books
+########################################
+
+# number of topics in a book, above which topics in navigation will be loaded dynamically downloaded
+loadBookAtOnceLimit=1000
+# suggested number of children levels dynamically loaded when a topic is expanded, value must be > 1
+dynamicLoadDepthsHint=3
+
+##############################
+# Customization for jsp pages
+##############################
+
+# /pluginid/dir or directory relative to directories inside org.eclipse.help.webapp plugin
+imagesDirectory=images
+
+# colors for advanced web UI implementation
+advanced.toolbarBackground=ButtonFace
+advanced.viewBackground=Window
+
+# fonts for advanced web UI implementation
+advanced.toolbarFont=icon
+advanced.viewFont=icon
+
+# colors for basic web UI implementation
+basic.toolbarBackground=#D4D0C8
+basic.viewBackground=#FFFFFF
+
+########################################
+# Customization for infocenter languages
+########################################
+
+# list of locales that infocenter will recognize and provide a customized content for; if locales
+# (or languages) accepted by client browser are not matched with any locales in this list,
+# the browser will be served content for default locale - the server locale, or locale specified
+# by eclipse -nl command line option; if list is not specified, the browser will be served contents
+# for its preferred locale; note: not providing this option may result in a large memory and disk
+# space requirements as navigations and indexes will be created for each distinct preferred locale
+# among browsers accessing the infocenter.
+# locales=en ja zh_CN zh_TW \ No newline at end of file
diff --git a/org.eclipse.help.base/schema/browser.exsd b/org.eclipse.help.base/schema/browser.exsd
new file mode 100644
index 000000000..5109f89ea
--- /dev/null
+++ b/org.eclipse.help.base/schema/browser.exsd
@@ -0,0 +1,166 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.help">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.help" id="browser" name="Browser"/>
+ </appInfo>
+ <documentation>
+ For providing web browsers capable of displaying html documents at a given URL.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="browser" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="browser">
+ <complexType>
+ <sequence>
+ <element ref="factoryclass"/>
+ </sequence>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ the unique ID of the browser.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="factoryclass" type="string">
+ <annotation>
+ <documentation>
+ the implementation class for the browser factory. This class must implement the &lt;samp&gt;org.eclipse.help.browser.IBrowserFactory&lt;/samp&gt; interface. This attribute may be omitted, and the nested &lt;samp&gt;factoryclass&lt;/samp&gt; element may be provided instead.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.help.browser.IBrowserFactory"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ the name of the browser (translatable).
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="factoryclass">
+ <complexType>
+ <sequence>
+ <element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ the implementation class for the browser factory. This class must implement the &lt;samp&gt;org.eclipse.help.browser.IBrowserFactory&lt;/samp&gt; interface.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.help.browser.IBrowserFactory"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameter">
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ name of a parameter passed to the implementation class
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="value" type="string" use="required">
+ <annotation>
+ <documentation>
+ value of a parameter passed to the implementation class
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ The following is a sample usage of the browser extension point:
+
+&lt;p&gt;
+&lt;pre&gt;
+ &lt;extension point=&quot;org.eclipse.help.browser&quot;&gt;
+ &lt;browser
+ id=&quot;org.eclipse.myPlugin.myBrowserID&quot;
+ factoryClass=&quot;org.eclipse.myPlugin.myPackage.MyFactoryClass&quot;
+ name=&quot;My Browser&quot;&gt;
+ &lt;/browser&gt;
+ &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The supplied factory class must implement the &lt;samp&gt;org.eclipse.help.browser.IBrowserFactory&lt;/samp&gt; interface.
+Methods in that interface determine whether the factory is available on the given system, i.e. is capable
+of supplying browser instances, and create browser instances that implement IBrowser interface.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The &lt;samp&gt;org.eclipse.help&lt;/samp&gt; and &lt;samp&gt;org.eclipse.help.ui&lt;/samp&gt; plug-ins contain implementation of browsers on common platforms.
+Other plug-ins can provide different implementations. In the preferences, the user can select the default
+browser from among available browsers.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2000, 2003 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Common Public License v1.0 which accompanies
+this distribution, and is available at &lt;a href=&quot;http://www.eclipse.org/legal/cpl-v10.html&quot;&gt;http://www.eclipse.org/legal/cpl-v10.html&lt;/a&gt;
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/org.eclipse.help.base/schema/luceneAnalyzer.exsd b/org.eclipse.help.base/schema/luceneAnalyzer.exsd
new file mode 100644
index 000000000..aac71e677
--- /dev/null
+++ b/org.eclipse.help.base/schema/luceneAnalyzer.exsd
@@ -0,0 +1,133 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.help">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.help" id="luceneAnalyzer" name="Lucene Analyzer"/>
+ </appInfo>
+ <documentation>
+ This extension point is used to register text analyzers for use by help when indexing and searching documentation.
+&lt;p&gt;
+Help exploits capabilities of the Lucene search engine, that allows indexing of token streams (streams of words).
+Analyzers create tokens from the character stream. They examine text content and provide tokens for use with the index.
+The text stream can be tokenized in many unique ways. A trivial analyzer can tokenize streams at white space,
+a different one can perform filtering of tokens, based on the application needs.
+Since the documentation is mostly human-readable text, it is desired that analyzers used by the help system
+perform language and grammar aware tokenization and normalization of indexed text.
+For some languages, the quality of search increases significantly if stop word removal and stemming is performed
+on the indexed text.
+&lt;p&gt;
+The analyzer contributed to this extension point will override the one provided by the Eclipse help system for a given locale.
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="analyzer" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="analyzer">
+ <complexType>
+ <attribute name="locale" type="string" use="required">
+ <annotation>
+ <documentation>
+ a string identifying locale for which the supplied analyzer is
+to bue sued.
+If two letters, language is provided, and the analyzer will be
+available to all locales of that language.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ a fully qualified name of the Java class extending &lt;samp&gt;org.apache.lucene.analysis.Analyzer&lt;/samp&gt;.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.apache.lucene.analysis.Analyzer"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ Following is an example of Lucene Analyzer configuration:
+
+&lt;p&gt;
+&lt;pre&gt;
+ &lt;extension id=&quot;com.xyx.XYZ&quot; point=&quot;org.eclipse.help.luceneAnalyzer&quot;&gt;
+ &lt;analyzer locale=&quot;ll_CC&quot; class=&quot;com.xyz.ll_CCAnalyzer&quot;/&gt;
+ &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The value of the &lt;samp&gt;locale&lt;/samp&gt; attribute must represent either a five- or two-charcter locale string.
+If the analyzer is configured for a language by specifying two-letter language designation, the analyzer is
+going to be used for all locales of this language. If the analyzer is configured that matchs a five-character
+locale, it is going to be used instead.
+&lt;p&gt;
+The value of the &lt;samp&gt;class&lt;/samp&gt; attribute must represent a class that extends &lt;samp&gt;org.apache.lucene.analysis.Analyzer&lt;/samp&gt;.
+It is recommended that this analyzer performs lowercase filtering for languages where it is possible to increase
+number of search hits by making search case-sensitive.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The Eclipse help system provides analyzers for all languages. For English and German, the analyzers perform stop word filtering, lowercase filtering, and stemming. For all the other languages the supplied analyzer only performs lowercase filtering.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2000, 2003 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Common Public License v1.0 which accompanies
+this distribution, and is available at &lt;a href=&quot;http://www.eclipse.org/legal/cpl-v10.html&quot;&gt;http://www.eclipse.org/legal/cpl-v10.html&lt;/a&gt;
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/org.eclipse.help.base/schema/webapp.exsd b/org.eclipse.help.base/schema/webapp.exsd
new file mode 100644
index 000000000..d59a4dfdb
--- /dev/null
+++ b/org.eclipse.help.base/schema/webapp.exsd
@@ -0,0 +1,110 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.help">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.help" id="webapp" name="webapp"/>
+ </appInfo>
+ <documentation>
+ Internal extension point for registering the name of the help web application plugin.
+&lt;p&gt; It is assumed that the web application root is the plugin directory &lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="webapp"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="webapp">
+ <complexType>
+ <attribute name="default" type="boolean" use="default" value="false">
+ <annotation>
+ <documentation>
+ When set to true, this is the default help web application plugin. Normally, only the platform contributed default implementation should set this attribute to true.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 2.1
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ The following is a sample usage of the webapp extension point:
+&lt;p&gt;
+&lt;samp&gt;
+&amp;lt;extension point=&quot;org.eclipse.help.webapp&quot;&amp;gt;
+&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;webapp default=&quot;true&quot; /&amp;gt;
+&lt;br&gt; &amp;lt;/extension&amp;gt;
+&lt;/samp&gt;
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ There is no API to implement. The plugin must be structured as a standard web application.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The Eclipse Platform provides the default implementation in the org.eclipse.help.webapp plugin.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2000, 2003 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Common Public License v1.0 which accompanies
+this distribution, and is available at &lt;a href=&quot;http://www.eclipse.org/legal/cpl-v10.html&quot;&gt;http://www.eclipse.org/legal/cpl-v10.html&lt;/a&gt;
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/org.eclipse.help.base/src/org/eclipse/help/browser/IBrowser.java b/org.eclipse.help.base/src/org/eclipse/help/browser/IBrowser.java
new file mode 100644
index 000000000..f99fdfd68
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/browser/IBrowser.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.browser;
+/**
+ * Represents a web browser that can be used
+ * by clients to display documents for the given URLs.
+ * @since 2.1
+ */
+public interface IBrowser {
+ /**
+ * Closes the browser.
+ */
+ public void close();
+ /**
+ * Queries the browser if close
+ * method is supported.
+ * @return true if the method is fully implemented
+ */
+ public boolean isCloseSupported();
+ /**
+ * Displays document with the given URL,
+ * and makes the browser visible.
+ * This method starts the browser if necessary.
+ * @param url the URL to display in the browser
+ */
+ public void displayURL(String url) throws Exception;
+ /**
+ * Queries the browser if setLocation
+ * method is supported.
+ * @return true if the method is fully implemented
+ */
+ public boolean isSetLocationSupported();
+ /**
+ * Queries the browser if setSize
+ * method is supported.
+ * @return true if the method is fully implemented
+ */
+ public boolean isSetSizeSupported();
+ /**
+ * Causes browser to be moved to the specified
+ * location. If the actual browser is not visible,
+ * the next time it becomes visible, it will be shown
+ * at the give location
+ * @param x horizontal coordinates of the left-top
+ * external corner
+ * @param y vertical coordinates of the left-top
+ * external corner
+ */
+ public void setLocation(int x, int y);
+ /**
+ * Causes browser to be resized to the specified
+ * size. If the actual browser is not visible,
+ * the next time it becomes visible, it will be shown
+ * with the give size.
+ * @param width width in pixels
+ * @param height height in pixels
+ * external corner
+ */
+ public void setSize(int width, int height);
+}
+
diff --git a/org.eclipse.help.base/src/org/eclipse/help/browser/IBrowserFactory.java b/org.eclipse.help.base/src/org/eclipse/help/browser/IBrowserFactory.java
new file mode 100644
index 000000000..acd5ff2c1
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/browser/IBrowserFactory.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.browser;
+/**
+ * Implementators of org.eclipse.help.browser
+ * extension points must provide implementation of this
+ * interface.
+ * @since 2.1
+ */
+public interface IBrowserFactory {
+ /**
+ * Checks whether the factory can work on the user system.
+ * @return false if the factory cannot work on this system;
+ * for example the required native browser required
+ * by browser adapters that it creates is not installed.
+ */
+ public boolean isAvailable();
+ /**
+ * Obtains a new instance of a web browser.
+ * @return instance of IBrowser
+ */
+ public IBrowser createBrowser();
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/browser/package.html b/org.eclipse.help.base/src/org/eclipse/help/browser/package.html
new file mode 100644
index 000000000..656a98351
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/browser/package.html
@@ -0,0 +1,40 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides interfaces that need to be implemented by
+web browser adapters.
+<h2>
+Package Specification</h2>
+Help System, requires web browser to display help document.&nbsp; Since
+availability of web browsers differ from system to system, it is possible
+to configure browser adapters in addition to ones provided by Help System.&nbsp; The browser (or browser adapter) can be configured by
+providing extension for <tt>org.eclipse.help.browser</tt> extension point.&nbsp;
+If more than one browser is available on a user system, the default browser
+can be designated in Help preference page.
+<p>In general terms, implementing a web browser involves:
+<ul>
+<li>
+Implementing standalone UI element having the <tt>IBrowser</tt> API, and
+capable of displaying HTML documents given a URL.</li>
+
+<li>
+Providing a factory for managing of browsers on a particular system, by
+implementing <tt>IBrowserFactory</tt> interface.</li>
+
+<li>
+Declaring an extension of the <tt>org.eclipse.help.browser</tt> extension
+point that mentions the name of that factory class.</li>
+</ul>
+Note that browser factory will be created on every system.&nbsp; If the
+factory is not capable of creating a browser on some system configurations,
+due to missing prerequisites e.t.c., its <tt>isAvailable()</tt> method
+should be returning false on these systems.
+
+</body>
+</html>
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/DefaultHelpSupport.java b/org.eclipse.help.base/src/org/eclipse/help/internal/DefaultHelpSupport.java
new file mode 100644
index 000000000..5016458e9
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/DefaultHelpSupport.java
@@ -0,0 +1,276 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal;
+import java.net.*;
+
+import org.eclipse.core.boot.*;
+import org.eclipse.help.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.appserver.*;
+import org.eclipse.help.internal.context.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * This class is the default implementation of the pluggable help support.
+ * In is registered into the support extension point, and all
+ * requests to display help are delegated to this class.
+ */
+public class DefaultHelpSupport implements IHelp {
+
+ /**
+ * BaseHelpViewer constructor.
+ */
+ public DefaultHelpSupport() {
+ super();
+ }
+
+ /**
+ * Displays help.
+ */
+ public void displayHelp() {
+ // Do not start help view if documentaton is not available, display error
+ if (getTocs().length == 0) {
+ // There is no documentation
+ HelpSystem.getDefaultErrorUtil().displayError(
+ Resources.getString("WW001"));
+ //Documentation is not installed.
+ return;
+ }
+
+ displayHelpURL(null);
+ }
+
+ /**
+ * Displays context-sensitive help for specified context
+ * @param contexts the context to display
+ * @param x int positioning information
+ * @param y int positioning information
+ */
+ public void displayContext(IContext context, int x, int y) {
+ // no implementation
+ }
+
+ /**
+ * Displays context-sensitive help for specified context
+ * @param contextIds context identifier
+ * @param x int positioning information
+ * @param y int positioning information
+ */
+ public void displayContext(String contextId, int x, int y) {
+ IContext context = HelpCore.getContextManager().getContext(contextId);
+ displayContext(context, x, y);
+ }
+
+ /**
+ * Displays a help resource
+ */
+ public void displayHelpResource(IHelpResource helpResource) {
+ if (helpResource instanceof IToc)
+ displayHelpURL("toc=" + URLEncoder.encode(helpResource.getHref()));
+ else if (helpResource instanceof ITopic)
+ displayHelpURL(
+ "topic="
+ + URLEncoder.encode(getTopicURL(helpResource.getHref())));
+ else
+ displayHelpResource(helpResource.getHref());
+ }
+
+ /**
+ * Displays a help resource specified as a url.
+ * <ul>
+ * <li>a URL in a format that can be returned by
+ * {@link org.eclipse.help.IHelpResource#getHref() IHelpResource.getHref()}
+ * <li>a URL query in the format format <em>key=value&amp;key=value ...</em>
+ * The valid keys are: "tab", "toc", "topic", "contextId".
+ * For example, <em>toc="/myplugin/mytoc.xml"&amp;topic="/myplugin/references/myclass.html"</em>
+ * is valid.
+ * </ul>
+ */
+ public void displayHelpResource(String href) {
+ // check if this is a toc
+ IToc toc = HelpCore.getTocManager().getToc(href, BootLoader.getNL());
+ if (toc != null)
+ displayHelpResource(toc);
+ else if (
+ href != null
+ && (href.startsWith("tab=")
+ || href.startsWith("toc=")
+ || href.startsWith("topic=")
+ || href.startsWith(
+ "contextId="))) { // assume it is a query string
+ displayHelpURL(href);
+ } else // assume this is a topic
+ displayHelpURL("topic=" + URLEncoder.encode(href));
+ }
+
+ /**
+ * Displays the specified table of contents.
+ */
+ public void displayHelp(String tocFileHref) {
+ displayHelp(tocFileHref, null);
+ }
+ /**
+ * Display help and selected specified topic.
+ */
+ public void displayHelp(
+ String toc,
+ String topic) { // Do not start help view if documentaton is not available, display error
+ if (getTocs().length == 0) {
+ // There is no documentation
+ HelpSystem.getDefaultErrorUtil().displayError(
+ Resources.getString("WW001"));
+ //Documentation is not installed.
+ return;
+ }
+
+ String query = null;
+ if (toc != null) {
+ query = "toc=" + toc;
+ if (topic != null)
+ query =
+ query + "&topic=" + URLEncoder.encode(getTopicURL(topic));
+ } else {
+ if (topic != null)
+ query = "topic=" + URLEncoder.encode(getTopicURL(topic));
+ }
+
+ displayHelpURL(query);
+ }
+ /**
+ * Displays context-sensitive help for specified context
+ * @deprecated
+ */
+ public void displayHelp(String contextId, int x, int y) {
+ displayContext(contextId, x, y);
+ }
+ /**
+ * Displays context-sensitive help for specified context
+ * @deprecated
+ */
+ public void displayHelp(IContext context, int x, int y) {
+ displayContext(context, x, y);
+ }
+ /**
+ * Display help for the a given topic and related topics.
+ * @param topic topic to be displayed by the help browse
+ * @param relatedTopics topics that will populate related topics view
+ */
+ public void displayHelp(IContext context, IHelpResource topic) {
+ if (context == null || topic == null || topic.getHref() == null)
+ return;
+ String url =
+ "tab=links"
+ + "&contextId="
+ + URLEncoder.encode(getContextID(context))
+ + "&topic="
+ + URLEncoder.encode(getTopicURL(topic.getHref()));
+ displayHelpURL(url);
+ }
+ /**
+ * Display help to search view for given query
+ * and selected topic.
+ * @param query search query in URL format key=value&key=value
+ * @param topic selected from the search results
+ */
+ public void displaySearch(String searchQuery, String topic) {
+ if (searchQuery == null || topic == null)
+ return;
+ String url =
+ "tab=search&"
+ + searchQuery
+ + "&topic="
+ + URLEncoder.encode(getTopicURL(topic));
+ displayHelpURL(url);
+ }
+ /**
+ * Displays the specified url.
+ * The url can contain query parameters to identify how help displays the document
+ */
+ void displayHelpURL(String helpURL) {
+ if (!HelpSystem.ensureWebappRunning()) {
+ HelpSystem.getDefaultErrorUtil().displayError(
+ Resources.getString("E043"));
+ return;
+ }
+
+ try {
+ if (helpURL == null || helpURL.length() == 0) {
+ HelpSystem.getHelpBrowser().displayURL(getBaseURL());
+ } else if (
+ helpURL.startsWith("tab=")
+ || helpURL.startsWith("toc=")
+ || helpURL.startsWith("topic=")
+ || helpURL.startsWith("contextId=")) {
+ HelpSystem.getHelpBrowser().displayURL(
+ getBaseURL() + "?" + helpURL);
+ } else {
+ HelpSystem.getHelpBrowser().displayURL(helpURL);
+ }
+ } catch (Exception e) {
+ HelpSystem.getDefaultErrorUtil().displayError(e.getMessage());
+ }
+ }
+ /**
+ * Computes context information for a given context ID.
+ * @param contextID java.lang.String ID of the context
+ * @return IContext
+ */
+ public IContext getContext(String contextID) {
+ //return HelpSystem.getContextManager().getContext(contextID);
+ return new ContextProxy(contextID);
+ }
+ /**
+ * Returns the list of all integrated tables of contents available.
+ * @return an array of TOC's
+ */
+ public IToc[] getTocs() {
+ return HelpCore.getTocManager().getTocs(BootLoader.getNL());
+ }
+
+ /**
+ * Returns <code>true</code> if the context-sensitive help
+ * window is currently being displayed, <code>false</code> if not.
+ */
+ public boolean isContextHelpDisplayed() {
+ return false;
+ }
+
+ private String getContextID(IContext context) {
+ if (context instanceof Context)
+ return ((Context) context).getID();
+ if (context instanceof ContextProxy)
+ return ((ContextProxy) context).getID();
+ return HelpCore.getContextManager().addContext(context);
+ }
+
+ private String getBaseURL() {
+ return "http://"
+ + WebappManager.getHost()
+ + ":"
+ + WebappManager.getPort()
+ + "/help/index.jsp";
+ }
+
+ private String getTopicURL(String topic) {
+ if (topic == null)
+ return null;
+ if (topic.startsWith("../"))
+ topic = topic.substring(2);
+ /*
+ if (topic.startsWith("/")) {
+ String base = "http://" + AppServer.getHost() + ":" + AppServer.getPort();
+ base += "/help/content/help:";
+ topic = base + topic;
+ }
+ */
+ return topic;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/HelpApplication.java b/org.eclipse.help.base/src/org/eclipse/help/internal/HelpApplication.java
new file mode 100644
index 000000000..569795495
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/HelpApplication.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal;
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.boot.IPlatformRunnable;
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.appserver.WebappManager;
+
+/**
+ * Help application.
+ * Starts webserver and help web application for use
+ * by infocenter and stand-alone help.
+ * Application takes a parameter "mode", that can take values:
+ * "infocenter" - when help system should run as infocenter,
+ * "standalone" - when help system should run as standalone.
+ */
+public class HelpApplication
+ implements IPlatformRunnable, IExecutableExtension {
+ private static final int STATUS_EXITTING = 0;
+ private static final int STATUS_RESTARTING = 2;
+ private static final int STATUS_RUNNING = 1;
+ private static int status = STATUS_RUNNING;
+ /**
+ * Causes help service to stop and exit
+ */
+ public static void stop() {
+ status = STATUS_EXITTING;
+ }
+ /**
+ * Causes help service to exit and start again
+ */
+ public static void restart() {
+ if (status != STATUS_EXITTING) {
+ status = STATUS_RESTARTING;
+ }
+ }
+ /**
+ * Runs help service application.
+ */
+ public Object run(Object args) throws Exception {
+ if (!HelpSystem.ensureWebappRunning()) {
+ System.out.println(
+ "Help web application could not start. Check log file for details.");
+ return EXIT_OK;
+ }
+ writeHostAndPort();
+ // main program loop
+ while (status == STATUS_RUNNING) {
+ try {
+ Thread.sleep(250);
+ } catch (InterruptedException ie) {
+ break;
+ }
+ }
+
+ if (status == STATUS_RESTARTING) {
+ return EXIT_RESTART;
+ } else {
+ return EXIT_OK;
+ }
+ }
+ /**
+ * @see IExecutableExtension
+ */
+ public void setInitializationData(
+ IConfigurationElement configElement,
+ String propertyName,
+ Object data) {
+ String value = (String) ((Map) data).get("mode");
+ if ("infocenter".equalsIgnoreCase(value)) {
+ HelpSystem.setMode(HelpSystem.MODE_INFOCENTER);
+ } else if ("standalone".equalsIgnoreCase(value)) {
+ HelpSystem.setMode(HelpSystem.MODE_STANDALONE);
+ }
+ }
+ private void writeHostAndPort() throws IOException {
+ Properties p = new Properties();
+ p.put("host", WebappManager.getHost());
+ p.put("port", "" + WebappManager.getPort());
+
+ File workspace = Platform.getLocation().toFile();
+ File hostPortFile = new File(workspace, ".metadata/.connection");
+ hostPortFile.deleteOnExit();
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(hostPortFile);
+ p.store(out, null);
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ioe2) {
+ }
+ }
+ }
+
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/HelpBasePlugin.java b/org.eclipse.help.base/src/org/eclipse/help/internal/HelpBasePlugin.java
new file mode 100644
index 000000000..05ee82410
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/HelpBasePlugin.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal;
+import org.eclipse.core.runtime.*;
+/**
+ * Simple plugin for help system.
+ */
+public class HelpBasePlugin extends Plugin {
+ public final static String PLUGIN_ID = "org.eclipse.help.base";
+ // debug options
+ public static boolean DEBUG = false;
+ public static boolean DEBUG_SEARCH = false;
+
+ protected static HelpBasePlugin plugin;
+ /**
+ * Logs an Error message with an exception. Note that the message should already
+ * be localized to proper locale.
+ * ie: Resources.getString() should already have been called
+ */
+ public static synchronized void logError(String message, Throwable ex) {
+ if (message == null)
+ message = "";
+ Status errorStatus =
+ new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, message, ex);
+ HelpBasePlugin.getDefault().getLog().log(errorStatus);
+ }
+ /**
+ * Logs a Warning message with an exception. Note that the message should already
+ * be localized to proper local.
+ * ie: Resources.getString() should already have been called
+ */
+ public static synchronized void logWarning(String message) {
+ if (HelpBasePlugin.DEBUG) {
+ if (message == null)
+ message = "";
+ Status warningStatus =
+ new Status(
+ IStatus.WARNING,
+ PLUGIN_ID,
+ IStatus.OK,
+ message,
+ null);
+ HelpBasePlugin.getDefault().getLog().log(warningStatus);
+ }
+ }
+
+ /**
+ * HelpViewerPlugin constructor. It is called as part of plugin
+ * activation.
+ */
+ public HelpBasePlugin(IPluginDescriptor descriptor) {
+ super(descriptor);
+ plugin = this;
+ }
+ /**
+ * @return the singleton instance of the help plugin
+ */
+ public static HelpBasePlugin getDefault() {
+ return plugin;
+ }
+ /**
+ * Shuts down this plug-in and discards all plug-in state.
+ * <p>
+ * This method should be re-implemented in subclasses that need to do something
+ * when the plug-in is shut down. Implementors should call the inherited method
+ * to ensure that any system requirements can be met.
+ * </p>
+ * <p>
+ * Plug-in shutdown code should be robust. In particular, this method
+ * should always make an effort to shut down the plug-in. Furthermore,
+ * the code should not assume that the plug-in was started successfully,
+ * as this method will be invoked in the event of a failure during startup.
+ * </p>
+ * <p>
+ * Note 1: If a plug-in has been started, this method will be automatically
+ * invoked by the platform when the platform is shut down.
+ * </p>
+ * <p>
+ * Note 2: This method is intended to perform simple termination
+ * of the plug-in environment. The platform may terminate invocations
+ * that do not complete in a timely fashion.
+ * </p>
+ * <b>Cliens must never explicitly call this method.</b>
+ *
+ * @exception CoreException if this method fails to shut down
+ * this plug-in
+ */
+ public void shutdown() throws CoreException {
+ HelpSystem.shutdown();
+ }
+ /**
+ * Starts up this plug-in.
+ * <p>
+ * This method should be overridden in subclasses that need to do something
+ * when this plug-in is started. Implementors should call the inherited method
+ * to ensure that any system requirements can be met.
+ * </p>
+ * <p>
+ * If this method throws an exception, it is taken as an indication that
+ * plug-in initialization has failed; as a result, the plug-in will not
+ * be activated; moreover, the plug-in will be marked as disabled and
+ * ineligible for activation for the duration.
+ * </p>
+ * <p>
+ * Plug-in startup code should be robust. In the event of a startup failure,
+ * the plug-in's <code>shutdown</code> method will be invoked automatically,
+ * in an attempt to close open files, etc.
+ * </p>
+ * <p>
+ * Note 1: This method is automatically invoked by the platform
+ * the first time any code in the plug-in is executed.
+ * </p>
+ * <p>
+ * Note 2: This method is intended to perform simple initialization
+ * of the plug-in environment. The platform may terminate initializers
+ * that do not complete in a timely fashion.
+ * </p>
+ * <b>Cliens must never explicitly call this method.</b>
+ *
+ * @exception CoreException if this plug-in did not start up properly
+ */
+ public void startup() throws CoreException {
+ // Setup debugging options
+ DEBUG = isDebugging();
+ if (DEBUG) {
+ DEBUG_SEARCH = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.help.base/debug/search")); //$NON-NLS-1$
+ }
+
+ HelpSystem.startup();
+ }
+
+ /**
+ * Initializes the default preferences settings for this plug-in.
+ *
+ * @since 2.0
+ */
+ protected void initializeDefaultPluginPreferences() {
+ Preferences prefs = getPluginPreferences();
+
+ String os = System.getProperty("os.name").toLowerCase();
+ boolean isWindows = os.indexOf("windows") != -1;
+
+ if (isWindows)
+ prefs.setDefault(
+ "custom_browser_path",
+ "\"C:\\Program Files\\Internet Explorer\\IEXPLORE.EXE\" %1");
+ else
+ prefs.setDefault("custom_browser_path", "mozilla %1");
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/HelpSystem.java b/org.eclipse.help.base/src/org/eclipse/help/internal/HelpSystem.java
new file mode 100644
index 000000000..4094dc06a
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/HelpSystem.java
@@ -0,0 +1,321 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal;
+import java.util.*;
+
+import org.eclipse.core.boot.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.*;
+import org.eclipse.help.browser.*;
+import org.eclipse.help.internal.appserver.*;
+import org.eclipse.help.internal.browser.*;
+import org.eclipse.help.internal.search.*;
+import org.eclipse.help.internal.util.*;
+import org.eclipse.help.internal.workingset.*;
+
+/**
+ * The actual implementation of the help system plugin.
+ */
+public final class HelpSystem {
+ protected static final HelpSystem instance = new HelpSystem();
+
+ private final static String WEBAPP_EXTENSION_ID = "org.eclipse.help.webapp";
+ private static final String WEBAPP_DEFAULT_ATTRIBUTE = "default";
+
+ private static final String HELP_SUPPORT_EXTENSION_ID =
+ "org.eclipse.help.support";
+ private static final String HELP_SUPPORT_CLASS_ATTRIBUTE = "class";
+
+ public final static String BOOKMARKS = "bookmarks";
+ public final static String WORKING_SETS = "workingSets";
+ public final static String WORKING_SET = "workingSet";
+ public final static int MODE_WORKBENCH = 0;
+ public final static int MODE_INFOCENTER = 1;
+ public final static int MODE_STANDALONE = 2;
+
+ protected SearchManager searchManager;
+ protected HashMap workingSetManagers;
+ private int mode = MODE_WORKBENCH;
+ private boolean webappStarted = false;
+ private IErrorUtil defaultErrorMessenger;
+ private IBrowser browser;
+ private IHelp helpSupport = null;
+ private boolean webappRunning = false;
+
+ /**
+ * HelpSystem constructor comment.
+ */
+ private HelpSystem() {
+ super();
+ }
+ public static HelpSystem getInstance() {
+ return instance;
+ }
+ /**
+ * Used to obtain Search Manager
+ * @return instance of SearchManager
+ */
+ public static SearchManager getSearchManager() {
+ if (getInstance().searchManager == null) {
+ synchronized (HelpSystem.class) {
+ if (getInstance().searchManager == null) {
+ getInstance().searchManager = new SearchManager();
+ }
+ }
+ }
+ return getInstance().searchManager;
+ }
+ /**
+ * Used to obtain Working Set Manager
+ * @return instance of WorkingSetManager
+ */
+ public static WorkingSetManager getWorkingSetManager() {
+ return getWorkingSetManager(BootLoader.getNL());
+ }
+
+ public static synchronized WorkingSetManager getWorkingSetManager(String locale) {
+ if (getInstance().workingSetManagers == null) {
+ getInstance().workingSetManagers = new HashMap();
+ }
+ WorkingSetManager wsmgr =
+ (WorkingSetManager) getInstance().workingSetManagers.get(locale);
+ if (wsmgr == null) {
+ wsmgr = new WorkingSetManager(locale);
+ getInstance().workingSetManagers.put(locale, wsmgr);
+ }
+ return wsmgr;
+ }
+
+ public static synchronized IBrowser getHelpBrowser() {
+ if (getInstance().browser == null)
+ getInstance().browser =
+ BrowserManager.getInstance().createBrowser();
+ return getInstance().browser;
+ }
+
+ public static synchronized IHelp getHelpSupport() {
+ if (getInstance().helpSupport == null)
+ getInstance().helpSupport = getInstance().initHelpSupport();
+ return getInstance().helpSupport;
+ }
+ /**
+ */
+ public HelpSystem newInstance() {
+ return null;
+ }
+
+ /**
+ * Shuts down the Help System.
+ * @exception CoreException if this method fails to shut down
+ * this plug-in
+ */
+ public static void shutdown() throws CoreException {
+ if (HelpPlugin.DEBUG) {
+ System.out.println("Help System is shutting down.");
+ }
+ if (getInstance().searchManager != null) {
+ getInstance().searchManager.close();
+ }
+ // stop the web apps
+ WebappManager.stop("help");
+ if (getMode() != MODE_WORKBENCH)
+ WebappManager.stop("helpControl");
+
+ // close any browsers created
+ BrowserManager.getInstance().closeAll();
+
+ if (HelpPlugin.DEBUG) {
+ System.out.println("Help System is shut down.");
+ }
+ }
+ /**
+ * Called by Platform after loading the plugin
+ */
+ public static void startup() {
+ try {
+ setDefaultErrorUtil(new IErrorUtil() {
+ public void displayError(String msg) {
+ System.out.println(msg);
+ }
+
+ public void displayError(String msg, Thread uiThread) {
+ System.out.println(msg);
+ }
+
+ });
+ HelpPlugin.getDefault().getPluginPreferences();
+ } catch (Exception e) {
+ HelpPlugin.getDefault().getLog().log(
+ new Status(
+ Status.ERROR,
+ HelpPlugin
+ .getDefault()
+ .getDescriptor()
+ .getUniqueIdentifier(),
+ 0,
+ Resources.getString("E005"),
+ e));
+ }
+ if (HelpPlugin.DEBUG) {
+ System.out.println("Help System started.");
+ }
+ }
+ public static boolean ensureWebappRunning() {
+ if (!getInstance().webappStarted) {
+ getInstance().webappStarted = true;
+
+ String webappPlugin = getWebappPlugin();
+
+ if (getMode() != MODE_WORKBENCH) {
+ // start the help control web app
+ try {
+ WebappManager.start(
+ "helpControl",
+ webappPlugin,
+ Path.EMPTY);
+ } catch (CoreException e) {
+ HelpPlugin.logError(Resources.getString("E043"), e);
+ return false;
+ }
+ }
+ // start the help web app
+ try {
+ WebappManager.start("help", webappPlugin, Path.EMPTY);
+ } catch (CoreException e) {
+ HelpPlugin.logError(Resources.getString("E042"), e);
+ return false;
+ }
+ getInstance().webappRunning = true;
+
+ }
+ return getInstance().webappRunning;
+ }
+
+ /**
+ * Returns the mode.
+ * @return int
+ */
+ public static int getMode() {
+ return getInstance().mode;
+ }
+
+ /**
+ * Sets the mode.
+ * @param mode The mode to set
+ */
+ public static void setMode(int mode) {
+ getInstance().mode = mode;
+ }
+
+ /**
+ * Sets the error messenger
+ */
+ public static void setDefaultErrorUtil(IErrorUtil em) {
+ getInstance().defaultErrorMessenger = em;
+ }
+
+ /**
+ * Returns the default error messenger. When no UI is present, all
+ * errors are sent to System.out.
+ * @return IErrorMessenger
+ */
+ public static IErrorUtil getDefaultErrorUtil() {
+ return getInstance().defaultErrorMessenger;
+ }
+
+ /**
+ * Returns the plugin id that defines the help webapp
+ */
+ private static String getWebappPlugin() {
+
+ // get the webapp extension from the system plugin registry
+ IPluginRegistry pluginRegistry = Platform.getPluginRegistry();
+ IExtensionPoint point =
+ pluginRegistry.getExtensionPoint(WEBAPP_EXTENSION_ID);
+ if (point != null) {
+ IExtension[] extensions = point.getExtensions();
+ if (extensions.length != 0) {
+ // We need to pick up the non-default configuration
+ IConfigurationElement[] elements =
+ extensions[0].getConfigurationElements();
+
+ for (int i = 0; i < elements.length; i++) {
+ String defaultValue =
+ elements[i].getAttribute(WEBAPP_DEFAULT_ATTRIBUTE);
+ if (defaultValue == null || defaultValue.equals("false")) {
+ return elements[i]
+ .getDeclaringExtension()
+ .getDeclaringPluginDescriptor()
+ .getUniqueIdentifier();
+ }
+ }
+ // if reached this point, then then pick the first (default) webapp
+ if (elements.length > 0)
+ return elements[0]
+ .getDeclaringExtension()
+ .getDeclaringPluginDescriptor()
+ .getUniqueIdentifier();
+ }
+ }
+
+ // if all fails
+ return "org.eclipse.help.webapp";
+ }
+
+ /**
+ * Instantiate the help support
+ */
+ private IHelp initHelpSupport() {
+ if (helpSupport == null) {
+ IPluginRegistry pluginRegistry = Platform.getPluginRegistry();
+ IExtensionPoint point =
+ pluginRegistry.getExtensionPoint(HELP_SUPPORT_EXTENSION_ID);
+ if (point != null) {
+ IExtension[] extensions = point.getExtensions();
+ if (extensions.length != 0) {
+ // There should only be one extension/config element so we just take the first
+ IConfigurationElement[] elements =
+ extensions[0].getConfigurationElements();
+ if (elements.length != 0) { // Instantiate the help support
+ try {
+ helpSupport =
+ (IHelp) elements[0].createExecutableExtension(
+ HELP_SUPPORT_CLASS_ATTRIBUTE);
+ } catch (CoreException e) {
+ // may need to change this
+ HelpPlugin.getDefault().getLog().log(e.getStatus());
+ }
+ }
+ }
+ }
+ }
+
+ // if no extension point found or instantiated, use default impl
+ if (helpSupport == null)
+ helpSupport = new DefaultHelpSupport();
+
+ return helpSupport;
+ }
+ /**
+ * Obtains Name of the Eclipse product
+ * @return String
+ */
+ public static String getProductName() {
+ IPlatformConfiguration c = BootLoader.getCurrentPlatformConfiguration();
+ String primaryFeatureId = c.getPrimaryFeatureIdentifier();
+ IPluginDescriptor pfd =
+ Platform.getPluginRegistry().getPluginDescriptor(primaryFeatureId);
+ if (pfd == null)
+ return ""; // no primary feature installed
+ return pfd.getLabel();
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/IndexToolApplication.java b/org.eclipse.help.base/src/org/eclipse/help/internal/IndexToolApplication.java
new file mode 100644
index 000000000..25c680f50
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/IndexToolApplication.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+import org.eclipse.core.boot.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.internal.*;
+
+/**
+ * application org.eclipse.help.indexTool
+ */
+public class IndexToolApplication
+ implements IPlatformRunnable, IExecutableExtension {
+
+ /**
+ * Constructor for IndexToolApplication.
+ */
+ public IndexToolApplication() {
+ super();
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
+ */
+ public void setInitializationData(
+ IConfigurationElement config,
+ String propertyName,
+ Object data)
+ throws CoreException {
+ }
+
+ /**
+ * @see org.eclipse.core.boot.IPlatformRunnable#run(java.lang.Object)
+ */
+ public Object run(Object args) throws Exception {
+ try {
+ String directory = System.getProperty("indexOutput");
+ if (directory == null || directory.length() == 0) {
+ throw new Exception("indexOutput property is not set.");
+ }
+ String localeStr = System.getProperty("indexLocale");
+ if (localeStr == null || localeStr.length() < 2) {
+ throw new Exception("indexLocale property is not set.");
+ }
+ Locale locale;
+ if (localeStr.length() >= 5) {
+ locale =
+ new Locale(
+ localeStr.substring(0, 2),
+ localeStr.substring(3, 5));
+ } else {
+ locale = new Locale(localeStr.substring(0, 2), "");
+ }
+ preindex(directory, locale);
+ } catch (Exception e) {
+ System.out.println(e);
+ e.printStackTrace();
+ HelpBasePlugin.logError("Preindexing failed", e);
+ }
+ return EXIT_OK;
+ }
+
+ private void preindex(String outputDir, Locale locale) throws Exception {
+ String helpStatePath =
+ HelpBasePlugin.getDefault().getStateLocation().toOSString();
+ String relIndexPath = "nl" + File.separator + locale.toString();
+ File indexPath =
+ new File(helpStatePath + File.separator + relIndexPath);
+ // clean
+ if (indexPath.exists()) {
+ delete(indexPath);
+ }
+ // index
+ HelpSystem.getSearchManager().updateIndex(
+ new NullProgressMonitor(),
+ HelpSystem.getSearchManager().getIndex(locale.toString()));
+ // zip up
+ File d =
+ new File(outputDir, "nl" + File.separator + locale.getLanguage());
+ if (locale.getCountry().length() > 0) {
+ d = new File(d, locale.getCountry());
+ }
+ if (!d.exists())
+ d.mkdirs();
+ ZipOutputStream zout =
+ new ZipOutputStream(
+ new FileOutputStream(new File(d, "doc_index.zip")));
+ try {
+ zipDirectory(indexPath, zout, null);
+ } finally {
+ zout.close();
+ }
+ }
+ /**
+ * Recursively deletes directory and files.
+ * @param file
+ * @throws IOException
+ */
+ private static void delete(File file) throws IOException {
+ if (file.isDirectory()) {
+ File files[] = file.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ delete(files[i]);
+ }
+ }
+ if (!file.delete()) {
+ throw new IOException("Cannot delete file " + file);
+ }
+ }
+ /**
+ * Adds files in a directory to a zip stream
+ * @param dir directory with files to zip
+ * @param zout ZipOutputStream
+ * @param base directory prefix for file entries inside the zip or null
+ * @throws Exception
+ */
+ private static void zipDirectory(
+ File dir,
+ ZipOutputStream zout,
+ String base)
+ throws IOException {
+ byte buffer[] = new byte[8192];
+ String[] files = dir.list();
+ if (files == null || files.length == 0)
+ return;
+ for (int i = 0; i < files.length; i++) {
+ String path;
+ if (base == null) {
+ path = files[i];
+ } else {
+ path = base + "/" + files[i];
+ }
+ File f = new File(dir, files[i]);
+ if (f.isDirectory())
+ zipDirectory(f, zout, path);
+ else {
+ ZipEntry zentry = new ZipEntry(path);
+ zout.putNextEntry(zentry);
+ FileInputStream inputStream = new FileInputStream(f);
+ int len;
+ while ((len = inputStream.read(buffer)) != -1)
+ zout.write(buffer, 0, len);
+ inputStream.close();
+ zout.flush();
+ zout.closeEntry();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserDescriptor.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserDescriptor.java
new file mode 100644
index 000000000..94663efb8
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserDescriptor.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+import org.eclipse.help.browser.IBrowserFactory;
+
+public class BrowserDescriptor {
+ private String browserID;
+ private String browserLabel;
+ private IBrowserFactory factory;
+ /**
+ * @param id ID of a browser as specified in plugin.xml
+ * @param label name of the browser
+ * @param factory the factory that creates instances
+ * of this browser
+ */
+ public BrowserDescriptor(String id, String label, IBrowserFactory factory) {
+ this.browserID = id;
+ this.browserLabel = label;
+ this.factory = factory;
+ }
+ public String getID() {
+ return browserID;
+ }
+ public String getLabel() {
+ return browserLabel;
+ }
+ public IBrowserFactory getFactory() {
+ return factory;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserLog.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserLog.java
new file mode 100644
index 000000000..668c41fae
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserLog.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+
+import java.io.*;
+import java.text.*;
+import java.util.Date;
+
+import org.eclipse.help.internal.HelpBasePlugin;
+
+/**
+ * Log for messages output by external browser processes.
+ */
+public class BrowserLog {
+ private static BrowserLog instance;
+ private String logFileName;
+ private boolean newSession;
+ DateFormat formatter = new SimpleDateFormat("MMM dd, yyyy kk:mm:ss.SS");
+ String LN=System.getProperty("line.separator");
+ /**
+ * Constructor
+ */
+ private BrowserLog() {
+ try {
+ newSession = true;
+ logFileName =
+ HelpBasePlugin
+ .getDefault()
+ .getStateLocation()
+ .append("browser.log")
+ .toOSString();
+ } catch (Exception e) {
+ // can get here if platform is shutting down
+ }
+ }
+ /**
+ * Obtains singleton
+ */
+ private static BrowserLog getInstance() {
+ if (instance == null) {
+ instance = new BrowserLog();
+ }
+ return instance;
+ }
+ /**
+ * Appends a line to the browser.log
+ */
+ public static synchronized void log(String message) {
+ getInstance().append(message);
+ }
+ private void append(String message) {
+ if (logFileName == null) {
+ return;
+ }
+ Writer outWriter = null;
+ try {
+ outWriter =
+ new BufferedWriter(
+ new OutputStreamWriter(
+ new FileOutputStream(logFileName, true),
+ "UTF-8"));
+ if (newSession) {
+ newSession = false;
+ outWriter.write(
+ LN + formatter.format(new Date()) + " NEW SESSION"+LN);
+ }
+ outWriter.write(
+ formatter.format(new Date()) + " " + message + LN);
+ outWriter.flush();
+ outWriter.close();
+ } catch (Exception e) {
+ if (outWriter != null) {
+ try {
+ outWriter.close();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserManager.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserManager.java
new file mode 100644
index 000000000..c1151906d
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/BrowserManager.java
@@ -0,0 +1,288 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.browser.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Creates browser by delegating
+ * to appropriate browser adapter
+ */
+public class BrowserManager {
+ public static final String DEFAULT_BROWSER_ID_KEY = "default_browser";
+ private static BrowserManager instance;
+ private boolean initialized = false;
+ private BrowserDescriptor currentBrowserDesc;
+ private BrowserDescriptor defaultBrowserDesc;
+ private BrowserDescriptor[] browsersDescriptors;
+ private Collection browsers = new ArrayList();
+ /**
+ * Private Constructor
+ */
+ private BrowserManager() {
+ }
+ /**
+ * Initialize
+ */
+ private void init() {
+ initialized = true;
+
+ // Find all available browsers
+ browsersDescriptors = createBrowserDescriptors();
+
+ // 1. set default browser from preferences
+ String defBrowserID =
+ HelpBasePlugin.getDefault().getPluginPreferences().getDefaultString(
+ DEFAULT_BROWSER_ID_KEY);
+ if (defBrowserID != null && (!"".equals(defBrowserID))) {
+ setDefaultBrowserID(defBrowserID);
+ }
+
+ if (defaultBrowserDesc == null) {
+ // No default browser in properties!
+ // Set default browser to prefered implementation
+ if (System.getProperty("os.name").startsWith("Win")) {
+ setDefaultBrowserID("org.eclipse.help.ui.iexplorer");
+ if (defaultBrowserDesc == null) {
+ setDefaultBrowserID("org.eclipse.help.ui.systembrowser");
+ }
+ if (defaultBrowserDesc == null) {
+ setDefaultBrowserID("org.eclipse.help.custombrowser");
+ }
+
+ } else if (System.getProperty("os.name").startsWith("Linux")) {
+ setDefaultBrowserID("org.eclipse.help.mozillaLinux");
+ if (defaultBrowserDesc == null) {
+ setDefaultBrowserID("org.eclipse.help.netscapeLinux");
+ }
+
+ } else if (System.getProperty("os.name").startsWith("SunOS")) {
+ setDefaultBrowserID("org.eclipse.help.netscapeSolaris");
+ } else if (System.getProperty("os.name").startsWith("AIX")) {
+ setDefaultBrowserID("org.eclipse.help.netscapeAIX");
+ } else if (
+ System.getProperty("os.name").toLowerCase().startsWith("hp")) {
+ setDefaultBrowserID("org.eclipse.help.mozillaHPUX");
+ if (defaultBrowserDesc == null) {
+ setDefaultBrowserID("org.eclipse.help.netscapeHPUX");
+ }
+ } else {
+ setDefaultBrowserID("org.eclipse.help.mozillaLinux");
+ }
+ }
+ if (defaultBrowserDesc == null) {
+ // No default browser in properties!
+ // Set default browser to one of the available
+ if (browsersDescriptors.length > 0)
+ defaultBrowserDesc = browsersDescriptors[0];
+ }
+ if (defaultBrowserDesc == null) {
+ // If no browsers at all, use the Null Browser Adapter
+ defaultBrowserDesc =
+ new BrowserDescriptor(
+ "",
+ "Null Browser",
+ new IBrowserFactory() {
+ public boolean isAvailable() {
+ return true;
+ }
+ public IBrowser createBrowser() {
+ return new IBrowser() {
+ public void close() {
+ }
+ public void displayURL(String url) {
+ String msg =
+ Resources.getString("no_browsers", url);
+ HelpBasePlugin.logError(msg, null);
+ HelpSystem.getDefaultErrorUtil().displayError(msg);
+ }
+ public boolean isCloseSupported() {
+ return false;
+ }
+ public boolean isSetLocationSupported() {
+ return false;
+ }
+ public boolean isSetSizeSupported() {
+ return false;
+ }
+ public void setLocation(int width, int height) {
+ }
+ public void setSize(int x, int y) {
+ }
+ };
+ }
+ });
+ }
+
+ // initialize current browser
+ String curBrowserID =
+ HelpBasePlugin.getDefault().getPluginPreferences().getString(
+ DEFAULT_BROWSER_ID_KEY);
+ if (curBrowserID != null && (!"".equals(curBrowserID))) {
+ setCurrentBrowserID(curBrowserID);
+ } else {
+ setCurrentBrowserID(getDefaultBrowserID());
+ }
+
+ }
+ /**
+ * Obtains singleton instance.
+ */
+ public static BrowserManager getInstance() {
+ if (instance == null)
+ instance = new BrowserManager();
+ return instance;
+ }
+ /**
+ * Creates all adapters, and returns
+ * available ones.
+ */
+ private BrowserDescriptor[] createBrowserDescriptors() {
+ if (this.browsersDescriptors != null)
+ return this.browsersDescriptors;
+ Collection bDescriptors = new ArrayList();
+ IConfigurationElement configElements[] =
+ Platform.getPluginRegistry().getConfigurationElementsFor(
+ "org.eclipse.help",
+ "browser");
+ for (int i = 0; i < configElements.length; i++) {
+ if (!configElements[i].getName().equals("browser"))
+ continue;
+ String id = configElements[i].getAttribute("id");
+ if (id == null)
+ continue;
+ String label = configElements[i].getAttribute("name");
+ if (label == null)
+ continue;
+ try {
+ Object adapter =
+ configElements[i].createExecutableExtension("factoryclass");
+ if (!(adapter instanceof IBrowserFactory))
+ continue;
+ if (((IBrowserFactory) adapter).isAvailable()) {
+ bDescriptors.add(
+ new BrowserDescriptor(
+ id,
+ label,
+ (IBrowserFactory) adapter));
+ }
+ } catch (CoreException ce) {
+ }
+ }
+ this.browsersDescriptors =
+ (BrowserDescriptor[]) bDescriptors.toArray(
+ new BrowserDescriptor[bDescriptors.size()]);
+ return this.browsersDescriptors;
+ }
+ /**
+ * Obtains browsers descriptors.
+ */
+ public BrowserDescriptor[] getBrowserDescriptors() {
+ if (!initialized) {
+ init();
+ }
+ return this.browsersDescriptors;
+ }
+ /**
+ * Gets the currentBrowserID.
+ * @return Returns a String or null if not set
+ */
+ public String getCurrentBrowserID() {
+ if (!initialized) {
+ init();
+ }
+ if (currentBrowserDesc == null)
+ return null;
+ return currentBrowserDesc.getID();
+ }
+ /**
+ * Gets the currentBrowserID.
+ * @return Returns a String or null if not set
+ */
+ public String getDefaultBrowserID() {
+ if (!initialized) {
+ init();
+ }
+ if (defaultBrowserDesc == null)
+ return null;
+ return defaultBrowserDesc.getID();
+ }
+ /**
+ * Sets the currentBrowserID.
+ * If browser of given ID does not exists,
+ * the method does nothing
+ * @param currentAdapterrID The ID of the adapter to to set as current
+ */
+ public void setCurrentBrowserID(String currentAdapterID) {
+ if (!initialized) {
+ init();
+ }
+ for (int i = 0; i < browsersDescriptors.length; i++) {
+ if (browsersDescriptors[i].getID().equals(currentAdapterID)) {
+ currentBrowserDesc = browsersDescriptors[i];
+ return;
+ }
+ }
+ }
+ /**
+ * Sets the defaultBrowserID.
+ * If browser of given ID does not exists,
+ * the method does nothing
+ * @param currentAdapterrID The ID of the adapter to to set as current
+ */
+ private void setDefaultBrowserID(String defaultAdapterID) {
+ if (!initialized) {
+ init();
+ }
+ for (int i = 0; i < browsersDescriptors.length; i++) {
+ if (browsersDescriptors[i].getID().equals(defaultAdapterID)) {
+ defaultBrowserDesc = browsersDescriptors[i];
+ return;
+ }
+ }
+ }
+ /**
+ * Creates web browser
+ */
+ public IBrowser createBrowser() {
+ if (!initialized) {
+ init();
+ }
+ return new CurrentBrowser(
+ createBrowserAdapter(),
+ getCurrentBrowserID());
+ }
+ /**
+ * Creates web browser
+ */
+ private IBrowser createBrowserAdapter() {
+ IBrowser browser = currentBrowserDesc.getFactory().createBrowser();
+ browsers.add(browser);
+ return browser;
+ }
+ /**
+ * Closes all browsers created
+ */
+ public void closeAll() {
+ if (!initialized) {
+ // nothing to do, do not initialize
+ return;
+ }
+ for (Iterator it = browsers.iterator(); it.hasNext();) {
+ IBrowser browser = (IBrowser) it.next();
+ browser.close();
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CurrentBrowser.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CurrentBrowser.java
new file mode 100644
index 000000000..e5f638b88
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CurrentBrowser.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+
+import org.eclipse.help.browser.IBrowser;
+
+/**
+ * Wrapper for individual browsers
+ * contributed through extension point.
+ */
+public class CurrentBrowser implements IBrowser {
+ private IBrowser browserAdapter;
+ private String browserAdapterId;
+ /**
+ * new adapter selected in preferences but not yet shown
+ */
+ private IBrowser newBrowserAdapter = null;
+ private String newBrowserAdapterId = null;
+ private boolean locationSet = false;
+ private boolean sizeSet = false;
+ private int x;
+ private int y;
+ private int width;
+ private int height;
+
+ public CurrentBrowser(IBrowser browserImpl, String browserAdapterId) {
+ this.browserAdapter = browserImpl;
+ this.browserAdapterId = browserAdapterId;
+ }
+ /**
+ * @see org.eclipse.help.browser.IBrowser#close()
+ */
+ public void close() {
+ browserAdapter.close();
+ }
+
+ /**
+ * @see org.eclipse.help.browser.IBrowser#isCloseSupported()
+ */
+ public boolean isCloseSupported() {
+ return browserAdapter.isCloseSupported();
+ }
+
+ /**
+ * @see org.eclipse.help.browser.IBrowser#displayURL(java.lang.String)
+ */
+ public void displayURL(String url) throws Exception {
+ checkDefaultAdapter();
+ if (newBrowserAdapter != null) {
+ browserAdapter.close();
+
+ browserAdapter = newBrowserAdapter;
+ newBrowserAdapter = null;
+ browserAdapterId = newBrowserAdapterId;
+ newBrowserAdapterId = null;
+
+ if (locationSet) {
+ browserAdapter.setLocation(x, y);
+ }
+ if (sizeSet) {
+ browserAdapter.setSize(width, height);
+ }
+ }
+ browserAdapter.displayURL(url);
+ }
+
+ /**
+ * @see org.eclipse.help.browser.IBrowser#isSetLocationSupported()
+ */
+ public boolean isSetLocationSupported() {
+ checkDefaultAdapter();
+ if (newBrowserAdapterId == null) {
+ return browserAdapter.isSetLocationSupported();
+ } else {
+ return browserAdapter.isSetLocationSupported()
+ || newBrowserAdapter.isSetLocationSupported();
+ }
+ }
+
+ /**
+ * @see org.eclipse.help.browser.IBrowser#isSetSizeSupported()
+ */
+ public boolean isSetSizeSupported() {
+ checkDefaultAdapter();
+ if (newBrowserAdapterId == null) {
+ return browserAdapter.isSetSizeSupported();
+ } else {
+ return browserAdapter.isSetSizeSupported()
+ || newBrowserAdapter.isSetSizeSupported();
+ }
+ }
+
+ /**
+ * @see org.eclipse.help.browser.IBrowser#setLocation(int, int)
+ */
+ public void setLocation(int x, int y) {
+ checkDefaultAdapter();
+ browserAdapter.setLocation(x, y);
+ locationSet = true;
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * @see org.eclipse.help.browser.IBrowser#setSize(int, int)
+ */
+ public void setSize(int width, int height) {
+ checkDefaultAdapter();
+ browserAdapter.setSize(width, height);
+ sizeSet = true;
+ this.width = width;
+ this.height = height;
+ }
+ /*
+ * Checks wheter default adapter has changed.
+ * If yes, sets the newBrowserAdapter field
+ */
+ private void checkDefaultAdapter() {
+ if (browserAdapterId
+ != BrowserManager.getInstance().getCurrentBrowserID()) {
+ newBrowserAdapter = BrowserManager.getInstance().createBrowser();
+ newBrowserAdapterId =
+ BrowserManager.getInstance().getCurrentBrowserID();
+ }
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowser.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowser.java
new file mode 100644
index 000000000..6e833afa0
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowser.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+
+import java.util.*;
+
+import org.eclipse.core.boot.*;
+import org.eclipse.help.browser.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ *
+ */
+public class CustomBrowser implements IBrowser {
+ public static final String CUSTOM_BROWSER_PATH_KEY = "custom_browser_path";
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#close()
+ */
+ public void close() {
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#isCloseSupported()
+ */
+ public boolean isCloseSupported() {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#displayURL(java.lang.String)
+ */
+ public void displayURL(String url) throws Exception {
+ String path =
+ HelpBasePlugin.getDefault().getPluginPreferences().getString(
+ CustomBrowser.CUSTOM_BROWSER_PATH_KEY);
+
+ String[] command = prepareCommand(path, url);
+
+ try {
+ Process pr = Runtime.getRuntime().exec(command);
+ Thread outConsumer = new StreamConsumer(pr.getInputStream());
+ outConsumer.setName("Custom browser adapter output reader");
+ outConsumer.start();
+ Thread errConsumer = new StreamConsumer(pr.getErrorStream());
+ errConsumer.setName("Custom browser adapter error reader");
+ errConsumer.start();
+ } catch (Exception e) {
+ HelpBasePlugin.logError(
+ Resources.getString("CustomBrowser.errorLaunching", url, path),
+ e);
+ throw new Exception(
+ Resources.getString("CustomBrowser.errorLaunching", url, path));
+ }
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#isSetLocationSupported()
+ */
+ public boolean isSetLocationSupported() {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#isSetSizeSupported()
+ */
+ public boolean isSetSizeSupported() {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#setLocation(int, int)
+ */
+ public void setLocation(int x, int y) {
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#setSize(int, int)
+ */
+ public void setSize(int width, int height) {
+ }
+
+ /**
+ * Creates the final command to launch.
+ * @param path
+ * @param url
+ * @return String[]
+ */
+ private String[] prepareCommand(String path, String url) {
+ ArrayList tokenList = new ArrayList();
+ //Divide along quotation marks
+ StringTokenizer qTokenizer =
+ new StringTokenizer(path.trim(), "\"", true);
+ boolean withinQuotation = false;
+ String quotedString = "";
+ while (qTokenizer.hasMoreTokens()) {
+ String curToken = qTokenizer.nextToken();
+ if (curToken.equals("\"")) {
+ if (withinQuotation) {
+ if (BootLoader
+ .OS_WIN32
+ .equalsIgnoreCase(BootLoader.getOS())) {
+ // need to quote URLs on Windows
+ tokenList.add("\"" + quotedString + "\"");
+ } else {
+ // qotes prevent launching on Unix 35673
+ tokenList.add(quotedString);
+ }
+ } else {
+ quotedString = "";
+ }
+ withinQuotation = !withinQuotation;
+ continue;
+ } else if (withinQuotation) {
+ quotedString = curToken;
+ continue;
+ } else {
+ //divide unquoted strings along white space
+ StringTokenizer parser = new StringTokenizer(curToken.trim());
+ while (parser.hasMoreTokens()) {
+ tokenList.add(parser.nextToken());
+ }
+ }
+ }
+ // substitute %1 by url
+ boolean substituted = false;
+ for (int i = 0; i < tokenList.size(); i++) {
+ String token = (String) tokenList.get(i);
+ if ("%1".equals(token)) {
+ tokenList.set(i, url);
+ substituted = true;
+ } else if ("\"%1\"".equals(token)) {
+ tokenList.set(i, "\"" + url + "\"");
+ substituted = true;
+ }
+ }
+ // add the url if not substituted already
+ if (!substituted)
+ tokenList.add(url);
+
+ String[] command = new String[tokenList.size()];
+ tokenList.toArray(command);
+ return command;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowserFactory.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowserFactory.java
new file mode 100644
index 000000000..5d61967bc
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/CustomBrowserFactory.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+
+import org.eclipse.help.browser.*;
+
+/**
+ * Produces Custom Browser
+ */
+public class CustomBrowserFactory implements IBrowserFactory {
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowserFactory#isAvailable()
+ */
+ public boolean isAvailable() {
+ return true;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowserFactory#createBrowser()
+ */
+ public IBrowser createBrowser() {
+ return new CustomBrowser();
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaBrowserAdapter.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaBrowserAdapter.java
new file mode 100644
index 000000000..2d03883c0
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaBrowserAdapter.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+import java.io.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.browser.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Browser adapter for browsers supporting
+ * -remote openURL command line option
+ * i.e. Mozilla and Netscape.
+ */
+public class MozillaBrowserAdapter implements IBrowser {
+ // delay that it takes mozilla to start responding
+ // to remote command after mozilla has been called
+ private static final int DELAY = 5000;
+ private long browserFullyOpenedAt = 0;
+ private BrowserThread lastBrowserThread = null;
+ private int x, y;
+ private int width, height;
+ private boolean setLocationPending = false;
+ private boolean setSizePending = false;
+ private String executable;
+ private String executableName;
+ private Thread uiThread;
+ /**
+ * Constructor
+ * @executable executable filename to launch
+ * @executableName name of the program to display when error occurs
+ */
+ MozillaBrowserAdapter(String executable, String executableName) {
+ this.uiThread = Thread.currentThread();
+ this.executable = executable;
+ this.executableName = executableName;
+ }
+ /*
+ * @see IBrowser#close()
+ */
+ public void close() {
+ }
+ /*
+ * @see IBrowser#displayURL(String)
+ */
+ public void displayURL(String url) {
+ if (lastBrowserThread != null)
+ lastBrowserThread.exitRequested = true;
+ if (setLocationPending || setSizePending) {
+ url = createPositioningURL(url);
+ }
+ lastBrowserThread = new BrowserThread(url);
+ lastBrowserThread.start();
+ setLocationPending = false;
+ setSizePending = false;
+ }
+ /*
+ * @see IBrowser#isCloseSupported()
+ */
+ public boolean isCloseSupported() {
+ return false;
+ }
+ /*
+ * @see IBrowser#isSetLocationSupported()
+ */
+ public boolean isSetLocationSupported() {
+ return true;
+ }
+ /*
+ * @see IBrowser#isSetSizeSupported()
+ */
+ public boolean isSetSizeSupported() {
+ return true;
+ }
+ /*
+ * @see IBrowser#setLocation(int, int)
+ */
+ public void setLocation(int x, int y) {
+ this.x = x;
+ this.y = y;
+ setLocationPending = true;
+ }
+ /*
+ * @see IBrowser#setSize(int, int)
+ */
+ public void setSize(int width, int height) {
+ this.width = width;
+ this.height = height;
+ setSizePending = true;
+ }
+ private synchronized String createPositioningURL(String url) {
+ IPath pluginPath = HelpBasePlugin.getDefault().getStateLocation();
+ File outFile =
+ pluginPath
+ .append("mozillaPositon")
+ .append("position.html")
+ .toFile();
+ try {
+ outFile.getParentFile().mkdirs();
+ PrintWriter writer =
+ new PrintWriter(
+ new BufferedWriter(
+ new OutputStreamWriter(
+ new FileOutputStream(outFile),
+ "UTF8")),
+ false);
+ writer.println(
+ "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">");
+ writer.println("<html><head>");
+ writer.println(
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">");
+ writer.print("<title></title><script language=\"JavaScript\">");
+ if (setSizePending)
+ writer.print("window.resizeTo(" + width + "," + height + ");");
+ if (setLocationPending)
+ writer.print("window.moveTo(" + x + "," + y + ");");
+ writer.print("location.replace(\"" + url + "\");");
+ writer.print("</script></head><body>");
+ writer.print("<a href=\"" + url + "\">--&gt;</a>");
+ writer.print("</body></html>");
+ writer.close();
+ return "file://" + outFile.getAbsolutePath();
+ } catch (IOException ioe) {
+ // return the original url
+ return url;
+ }
+ }
+ private class BrowserThread extends Thread {
+ public boolean exitRequested = false;
+ private String url;
+ public BrowserThread(String urlName) {
+ this.url = urlName;
+ }
+ /**
+ * @param browserCmd
+ * @return int 0 if success
+ */
+ private int openBrowser(String browserCmd) {
+ try {
+ Process pr = Runtime.getRuntime().exec(browserCmd);
+ StreamConsumer outputs =
+ new StreamConsumer(pr.getInputStream());
+ (outputs).start();
+ StreamConsumer errors = new StreamConsumer(pr.getErrorStream());
+ (errors).start();
+ pr.waitFor();
+ int ret = pr.exitValue();
+
+ if (ret == 0 && errorsInOutput(outputs, errors)) {
+ return -1;
+ }
+ return ret;
+ } catch (InterruptedException e) {
+ } catch (IOException e) {
+ String msg =
+ Resources.getString(
+ "MozillaBrowserAdapter.executeFailed",
+ executableName);
+ HelpBasePlugin.logError(msg, e);
+ HelpSystem.getDefaultErrorUtil().displayError(msg, uiThread);
+ // return success, so second command does not execute
+ return 0;
+ }
+ return -1;
+ }
+ /**
+ * On some OSes 0 is always returned by netscape -remote.
+ * It is necessary to examine ouput to find out failure
+ * @param outputs
+ * @param errors
+ * @return
+ * @throws InterruptedException
+ */
+ private boolean errorsInOutput(
+ StreamConsumer outputs,
+ StreamConsumer errors) {
+ try {
+ outputs.join(1000);
+ if (outputs.getLastLine() != null
+ && (outputs.getLastLine().indexOf("No running window found")
+ >= 0
+ || outputs.getLastLine().indexOf("not running on display")
+ >= 0)) {
+ return true;
+ }
+ errors.join(1000);
+ if (errors.getLastLine() != null
+ && (errors.getLastLine().indexOf("No running window found")
+ >= 0
+ || errors.getLastLine().indexOf("not running on display")
+ >= 0)) {
+ return true;
+ }
+ } catch (InterruptedException ie) {
+ // ignore
+ }
+ return false;
+ }
+ public void run() {
+ // If browser is opening, wait until it fully opens,
+ waitForBrowser();
+ if (exitRequested)
+ return;
+ if (openBrowser(executable + " -remote openURL(" + url + ")")
+ == 0) {
+ return;
+ }
+ if (exitRequested)
+ return;
+ browserFullyOpenedAt = System.currentTimeMillis() + DELAY;
+ openBrowser(executable + " " + url);
+ }
+ private void waitForBrowser() {
+ while (System.currentTimeMillis() < browserFullyOpenedAt)
+ try {
+ if (exitRequested)
+ return;
+ Thread.sleep(100);
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaFactory.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaFactory.java
new file mode 100644
index 000000000..fc8ec6de0
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/MozillaFactory.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.browser.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+public class MozillaFactory implements IBrowserFactory, IExecutableExtension {
+ private String executable;
+ private String executableName;
+ private String os;
+ private MozillaBrowserAdapter browserInstance = null;
+ /**
+ * Constructor.
+ */
+ public MozillaFactory() {
+ super();
+ }
+ /*
+ * @see IBrowserFactory#isAvailable()
+ */
+ public boolean isAvailable() {
+ if (!System
+ .getProperty("os.name")
+ .toLowerCase()
+ .startsWith(os.toLowerCase())) {
+ return false;
+ }
+ try {
+ Process pr = Runtime.getRuntime().exec("which " + executable);
+ StreamConsumer outputs = new StreamConsumer(pr.getInputStream());
+ (outputs).start();
+ StreamConsumer errors = new StreamConsumer(pr.getErrorStream());
+ (errors).start();
+ pr.waitFor();
+ int ret = pr.exitValue();
+ if (ret == 0) {
+ return !errorsInOutput(outputs, errors);
+ } else {
+ return false;
+ }
+ } catch (InterruptedException e) {
+ return false;
+ } catch (IOException e) {
+ // launching which failed, assume browser executable is present
+ return true;
+ }
+ }
+ /**
+ * On some OSes 0 is always returned by "which" command
+ * it is necessary to examine ouput to find out failure.
+ * @param outputs
+ * @param errors
+ * @return
+ * @throws InterruptedException
+ */
+ private boolean errorsInOutput(
+ StreamConsumer outputs,
+ StreamConsumer errors) {
+ try {
+ outputs.join(1000);
+ if (outputs.getLastLine() != null
+ && outputs.getLastLine().indexOf("no " + executable + " in")
+ >= 0) {
+ return true;
+ }
+ errors.join(1000);
+ if (errors.getLastLine() != null
+ && errors.getLastLine().indexOf("no " + executable + " in")
+ >= 0) {
+ return true;
+ }
+ } catch (InterruptedException ie) {
+ // ignore
+ }
+ return false;
+ }
+ /*
+ * @see IBrowserFactory#createBrowser()
+ */
+ public IBrowser createBrowser() {
+ // Create single browser for all clients
+ if (browserInstance == null) {
+ browserInstance =
+ new MozillaBrowserAdapter(executable, executableName);
+ }
+ return browserInstance;
+ }
+ /**
+ * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
+ */
+ public void setInitializationData(
+ IConfigurationElement config,
+ String propertyName,
+ Object data)
+ throws CoreException {
+ try {
+ Hashtable params = (Hashtable) data;
+ executable = (String) params.get("executable");
+ executableName = (String) params.get("executableName");
+ os = (String) params.get("os");
+ } catch (Exception e) {
+ throw new CoreException(
+ new Status(
+ IStatus.ERROR,
+ HelpBasePlugin.PLUGIN_ID,
+ IStatus.OK,
+ Resources.getString("MozillaFactory.dataMissing"),
+ e));
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/StreamConsumer.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/StreamConsumer.java
new file mode 100644
index 000000000..1b05dc864
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/StreamConsumer.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser;
+import java.io.*;
+
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Used to receive output from processes
+ */
+public class StreamConsumer extends Thread {
+ BufferedReader bReader;
+ private String lastLine;
+ public StreamConsumer(InputStream inputStream) {
+ super();
+ setDaemon(true);
+ bReader = new BufferedReader(new InputStreamReader(inputStream));
+ }
+ public void run() {
+ try {
+ String line;
+ while (null != (line = bReader.readLine())) {
+ lastLine = line;
+ BrowserLog.log(line);
+ }
+ bReader.close();
+ } catch (IOException ioe) {
+ HelpBasePlugin.logError(Resources.getString("WE001"), ioe);
+ }
+ }
+ /**
+ * @return last line obtained or null
+ */
+ public String getLastLine() {
+ return lastLine;
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserAdapter.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserAdapter.java
new file mode 100644
index 000000000..3b526241b
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserAdapter.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser.macosx;
+
+import java.io.IOException;
+
+import org.eclipse.help.browser.IBrowser;
+
+public class DefaultBrowserAdapter implements IBrowser {
+
+ private static DefaultBrowserAdapter fgInstance;
+
+ static DefaultBrowserAdapter getInstance() {
+ if (fgInstance == null)
+ fgInstance= new DefaultBrowserAdapter();
+ return fgInstance;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#close()
+ */
+ public void close() {
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#displayURL(String)
+ */
+ public void displayURL(String url) {
+ /*
+ * Code from Marc-Antoine Parent
+ */
+ try {
+ Runtime.getRuntime().exec(
+ new String[] {
+ "/usr/bin/osascript",
+ "-e",
+ "open location \"" + url +"\""
+ }
+ );
+ } catch (IOException e) {
+ // ignore silently
+ }
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#isCloseSupported()
+ */
+ public boolean isCloseSupported() {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#isSetLocationSupported()
+ */
+ public boolean isSetLocationSupported() {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#isSetSizeSupported()
+ */
+ public boolean isSetSizeSupported() {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#setLocation(int, int)
+ */
+ public void setLocation(int x, int y) {
+ }
+
+ /**
+ * @see org.eclipse.help.ui.browser.IBrowser#setSize(int, int)
+ */
+ public void setSize(int width, int height) {
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserFactory.java b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserFactory.java
new file mode 100644
index 000000000..1e4951122
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/browser/macosx/DefaultBrowserFactory.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.browser.macosx;
+
+import org.eclipse.help.browser.*;
+
+
+public class DefaultBrowserFactory implements IBrowserFactory {
+
+ public DefaultBrowserFactory() {
+ super();
+ }
+
+ /*
+ * @see IBrowserFactory#isAvailable()
+ */
+ public boolean isAvailable() {
+ return System.getProperty("os.name").equals("Mac OS X");
+ /*
+ * we assume that every Mac OS X has an "/usr/bin/osascript"
+ * so we don't test any further
+ */
+ }
+
+ /*
+ * @see IBrowserFactory#createBrowser()
+ */
+ public IBrowser createBrowser() {
+ return DefaultBrowserAdapter.getInstance();
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/ASCIIReader.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ASCIIReader.java
new file mode 100644
index 000000000..2cec12f93
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ASCIIReader.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.io.*;
+
+/**
+ * High performance reader. Assumes the input stream is ASCII.
+ */
+public class ASCIIReader extends Reader {
+ private InputStream stream;
+ int bufSize;
+ byte[] buf;
+ /**
+ * @param stream InputStream
+ * @param bufSize size of internal buffer
+ */
+ public ASCIIReader(InputStream stream, int bufSize) {
+ this.stream = stream;
+ this.bufSize = bufSize;
+ buf = new byte[bufSize];
+ }
+
+ /**
+ * @see java.io.Reader#read(char, int, int)
+ */
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ int n = stream.read(buf, 0, Math.min(bufSize, len));
+ for (int i = 0; i < n; i++) {
+ cbuf[off + i] = (char) buf[i];
+ }
+ return n;
+ }
+
+ /**
+ * @see java.io.Reader#close()
+ */
+ public void close() throws IOException {
+ stream.close();
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java
new file mode 100644
index 000000000..c2628d454
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/AnalyzerDescriptor.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import org.apache.lucene.analysis.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Text Analyzer Descriptor. Encapsulates Lucene Analyzer
+ */
+public class AnalyzerDescriptor {
+ private Analyzer luceneAnalyzer;
+ private String id;
+ private String lang;
+
+ /**
+ * Constructor
+ */
+ public AnalyzerDescriptor(String locale) {
+
+ // try creating the analyzer for the specified locale (usually lang_country)
+ this.luceneAnalyzer = createAnalyzer(locale);
+
+ // try creating configured analyzer for the language only
+ if (this.luceneAnalyzer == null) {
+ String language = null;
+ if (locale.length() > 2) {
+ language = locale.substring(0, 2);
+ this.luceneAnalyzer = createAnalyzer(language);
+ }
+ }
+
+ // if all fails, create default analyzer
+ if (this.luceneAnalyzer == null) {
+ this.id =
+ HelpBasePlugin.getDefault().getDescriptor().getUniqueIdentifier()
+ + "#"
+ + HelpBasePlugin
+ .getDefault()
+ .getDescriptor()
+ .getVersionIdentifier()
+ .toString();
+ this.luceneAnalyzer = new DefaultAnalyzer(locale);
+ this.lang = locale;
+ }
+
+ }
+ /**
+ * Gets the analyzer.
+ * @return Returns a Analyzer
+ */
+ public Analyzer getAnalyzer() {
+ return new SmartAnalyzer(lang, luceneAnalyzer);
+ }
+
+ /**
+ * Gets the id.
+ * @return Returns a String
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Gets the language for the analyzer
+ * @return Returns a String
+ */
+ public String getLang() {
+ return lang;
+ }
+
+ /**
+ * Creates analyzer for a locale,
+ * if it is configured in the org.eclipse.help.luceneAnalyzer
+ * extension point. The identifier of the analyzer and locale and lang are also set.
+ * @return Analyzer or null if no analyzer is configured
+ * for given locale.
+ */
+ private Analyzer createAnalyzer(String locale) {
+ // find extension point
+ IConfigurationElement configElements[] =
+ Platform.getPluginRegistry().getConfigurationElementsFor(
+ HelpBasePlugin.PLUGIN_ID,
+ "luceneAnalyzer");
+ for (int i = 0; i < configElements.length; i++) {
+ if (!configElements[i].getName().equals("analyzer"))
+ continue;
+ String analyzerLocale = configElements[i].getAttribute("locale");
+ if (analyzerLocale == null || !analyzerLocale.equals(locale))
+ continue;
+ try {
+ Object analyzer =
+ configElements[i].createExecutableExtension("class");
+ if (!(analyzer instanceof Analyzer))
+ continue;
+ else {
+ String pluginId =
+ configElements[i]
+ .getDeclaringExtension()
+ .getDeclaringPluginDescriptor()
+ .getUniqueIdentifier();
+ String pluginVersion =
+ configElements[i]
+ .getDeclaringExtension()
+ .getDeclaringPluginDescriptor()
+ .getVersionIdentifier()
+ .toString();
+ this.luceneAnalyzer = (Analyzer) analyzer;
+ this.id = pluginId + "#" + pluginVersion;
+ this.lang = locale;
+ if (HelpBasePlugin.PLUGIN_ID.equals(pluginId)) {
+ // The analyzer is contributed by help plugin.
+ // Continue in case there is another analyzer for the same locale
+ // let another analyzer take precendence over one from help
+ } else {
+ // the analyzer does not come from help
+ return this.luceneAnalyzer;
+ }
+ }
+ } catch (CoreException ce) {
+ HelpBasePlugin.logError(
+ Resources.getString(
+ "ES23",
+ configElements[i].getAttribute("class"),
+ locale),
+ ce);
+ }
+ }
+
+ return this.luceneAnalyzer;
+ }
+ /**
+ * Checks whether analyzer is compatible with a given analyzer
+ * @param analyzerId id of analyzer used in the past by the index;
+ * id has a form: pluginID#pluginVersion
+ * @return true when it is known that given analyzer is compatible with
+ * this analyzer
+ */
+ public boolean isCompatible(String analyzerId) {
+ if (id.equals(analyzerId)) {
+ return true;
+ }
+ // analyzers between versions 2.0.1 and 3.0.0 of org.eclipse.help plugin
+ // are compatible (logic unchanged), index can be preserved between them
+ if (analyzerId.compareTo(HelpBasePlugin.PLUGIN_ID + "#2.0.1") >= 0
+ && analyzerId.compareTo(HelpBasePlugin.PLUGIN_ID + "#3.0.0") <= 0
+ && id.compareTo(HelpBasePlugin.PLUGIN_ID + "#2.0.1") >= 0
+ && id.compareTo(HelpBasePlugin.PLUGIN_ID + "#3.0.0") <= 0) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/Analyzer_en.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/Analyzer_en.java
new file mode 100644
index 000000000..b15681516
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/Analyzer_en.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+import java.io.Reader;
+
+import org.apache.lucene.analysis.*;
+/**
+ * Lucene Analyzer for English.
+ * LowerCaseTokenizer->StopFilter->PorterStemFilter
+ */
+public class Analyzer_en extends Analyzer {
+ Analyzer stopAnalyzer;
+ /**
+ * Constructor for Analyzer_en.
+ */
+ public Analyzer_en() {
+ super();
+ stopAnalyzer = new StopAnalyzer(STOP_WORDS);
+ }
+ /**
+ * Creates a TokenStream which tokenizes all the text
+ * in the provided Reader.
+ */
+ public final TokenStream tokenStream(String fieldName, Reader reader) {
+ return new PorterStemFilter(stopAnalyzer.tokenStream(fieldName, reader));
+ }
+ /**
+ * Array of English stop words.
+ * Differs from StandardAnalyzer's default stop words by
+ * not having "for", "if", and "this" that are java keywords.
+ */
+ private final static String[] STOP_WORDS =
+ {
+ "a",
+ "and",
+ "are",
+ "as",
+ "at",
+ "be",
+ "but",
+ "by",
+ "in",
+ "into",
+ "is",
+ "it",
+ "no",
+ "not",
+ "of",
+ "on",
+ "or",
+ "s",
+ "such",
+ "t",
+ "that",
+ "the",
+ "their",
+ "then",
+ "there",
+ "these",
+ "they",
+ "to",
+ "was",
+ "will",
+ "with" };
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/DefaultAnalyzer.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/DefaultAnalyzer.java
new file mode 100644
index 000000000..c4cbe6d84
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/DefaultAnalyzer.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+import org.apache.lucene.analysis.*;
+import org.eclipse.core.boot.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+/**
+ * Lucene Analyzer.
+ * LowerCaseTokenizer->WordTokenStream (uses word breaking in java.text)
+ */
+public class DefaultAnalyzer extends Analyzer {
+ /**
+ * Constructor for Analyzer.
+ */
+ private Locale locale;
+ public DefaultAnalyzer(String localeString) {
+ super();
+ // Create a locale object for a given locale string
+ Locale userLocale = getLocale(localeString);
+
+ // Check if the locale is supported by BreakIterator
+ // check here to do it only once.
+ Locale[] availableLocales = BreakIterator.getAvailableLocales();
+ for (int i = 0; i < availableLocales.length; i++) {
+ if (userLocale.equals(availableLocales[i])) {
+ locale = userLocale;
+ break;
+ }
+ }
+ if (locale == null && userLocale.getDisplayVariant().length() > 0) {
+ // Check if the locale without variant is supported by BreakIterator
+ Locale countryLocale =
+ new Locale(userLocale.getLanguage(), userLocale.getCountry());
+ for (int i = 0; i < availableLocales.length; i++) {
+ if (countryLocale.equals(availableLocales[i])) {
+ locale = countryLocale;
+ break;
+ }
+ }
+ }
+ if (locale == null && userLocale.getCountry().length() > 0) {
+ // Check if at least the language is supported by BreakIterator
+ Locale language = new Locale(userLocale.getLanguage(), "");
+ for (int i = 0; i < availableLocales.length; i++) {
+ if (language.equals(availableLocales[i])) {
+ locale = language;
+ break;
+ }
+ }
+ }
+
+ if (locale == null) {
+ // Locale is not supported, will use en_US
+ HelpBasePlugin.logError(
+ Resources.getString("ES24", localeString),
+ null);
+ locale = new Locale("en", "US");
+ }
+ }
+ /**
+ * Creates a TokenStream which tokenizes all the text
+ * in the provided Reader.
+ */
+ public final TokenStream tokenStream(String fieldName, Reader reader) {
+ return new LowerCaseFilter(
+ new WordTokenStream(fieldName, reader, locale));
+ }
+
+ /**
+ * Creates a Locale object out of a string representation
+ */
+ private Locale getLocale(String clientLocale) {
+ if (clientLocale == null)
+ clientLocale = BootLoader.getNL();
+ if (clientLocale == null)
+ clientLocale = Locale.getDefault().toString();
+
+ // break the string into tokens to get the Locale object
+ StringTokenizer locales = new StringTokenizer(clientLocale, "_");
+ if (locales.countTokens() == 1)
+ return new Locale(locales.nextToken(), "");
+ else if (locales.countTokens() == 2)
+ return new Locale(locales.nextToken(), locales.nextToken());
+ else if (locales.countTokens() == 3)
+ return new Locale(
+ locales.nextToken(),
+ locales.nextToken(),
+ locales.nextToken());
+ else
+ return Locale.getDefault();
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/HTMLDocParser.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/HTMLDocParser.java
new file mode 100644
index 000000000..b9c7f589a
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/HTMLDocParser.java
@@ -0,0 +1,381 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.help.internal.search;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import org.apache.lucene.demo.html.*;
+import org.eclipse.help.internal.*;
+
+/**
+ * Parser HTML documents.
+ * Extracts document encoding from header,
+ * and delegates to lucene HTML parser for extraction
+ * of title, summary, and content.
+ */
+public class HTMLDocParser {
+ // maximum number of characters that will be searched
+ // from the beginning of HTML document to charset declaration
+ private static final int MAX_OFFSET = 2048;
+
+ // elements, atributes and values contstants
+ final String ELEMENT_META = "META";
+ final String ELEMENT_BODY = "body";
+ final String ELEMENT_HEAD = "head";
+ final String ATTRIBUTE_HTTP = "http-equiv";
+ final String ATTRIBUTE_HTTP_VALUE = "content-type";
+ final String ATTRIBUTE_CONTENT = "content";
+
+ // states for parsing elements
+ final int STATE_ELEMENT_START = 0;
+ final int STATE_ELEMENT_AFTER_LT = 1;
+ final int STATE_ELEMENT_AFTER_LT_SLASH = 2;
+ final int STATE_ELEMENT_META = 3;
+ // states for parsing HTTP-EQUIV attribute
+ final int STATE_HTTP_START = 0;
+ final int STATE_HTTP_AFTER_NAME = 1;
+ final int STATE_HTTP_AFTER_EQ = 2;
+ final int STATE_HTTP_DONE = 3;
+ // states for parsing CONTENT attribute
+ final int STATE_CONTENT_START = 0;
+ final int STATE_CONTENT_AFTER_NAME = 1;
+ final int STATE_CONTENT_AFTER_EQ = 2;
+ final int STATE_CONTENT_DONE = 3;
+
+ private HTMLParser htmlParser;
+ private InputStream inputStream = null;
+ /**
+ * @param url
+ * @throws IOException
+ */
+ public void openDocument(URL url) throws IOException {
+ inputStream = url.openStream();
+
+ String encoding = getCharsetFromHTML(inputStream);
+ try {
+ inputStream.close();
+ } catch (IOException closeIOE) {
+ }
+ inputStream = url.openStream();
+ if (encoding != null) {
+ try {
+ htmlParser =
+ new HTMLParser(
+ new InputStreamReader(inputStream, encoding));
+
+ } catch (UnsupportedEncodingException uee) {
+ if (HelpBasePlugin.DEBUG_SEARCH) {
+ System.out.println(
+ this.getClass().getName()
+ + " JVM does not support encoding "
+ + encoding
+ + " specified in document "
+ + url.getPath()
+ + ". Default encoding will be used during indexing.");
+ }
+ htmlParser = new HTMLParser(new InputStreamReader(inputStream));
+ }
+ } else {
+ if (HelpBasePlugin.DEBUG_SEARCH) {
+ System.out.println(
+ this.getClass().getName()
+ + " Encoding not found in document "
+ + url.getPath()
+ + ". Default encoding will be used during indexing.");
+ }
+ htmlParser = new HTMLParser(new InputStreamReader(inputStream));
+ }
+ }
+ /**
+ * Releases resources (closes streams)
+ */
+ public void closeDocument() {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException closeIOE) {
+ }
+ }
+ }
+ public String getTitle() throws IOException {
+ if (htmlParser == null) {
+ throw new NullPointerException();
+ }
+ try {
+ return htmlParser.getTitle();
+ } catch (InterruptedException ie) {
+ return "";
+ }
+ }
+ public String getSummary() throws IOException {
+ if (htmlParser == null) {
+ throw new NullPointerException();
+ }
+ try {
+ return htmlParser.getSummary();
+ } catch (InterruptedException ie) {
+ return "";
+ }
+ }
+ public Reader getContentReader() throws IOException {
+ if (htmlParser == null) {
+ throw new NullPointerException();
+ }
+ return htmlParser.getReader();
+ }
+ /**
+ * Private.
+ * Parses HTML to extract document encoding specified in HTTP
+ * equivalent META tag in the document header. Example of such META tag is
+ * <META HTTP-EQUIV="content-type" CONTENT="text/html; charset=UTF-8">
+ * @return String or null if encoding not found
+ */
+ public String getCharsetFromHTML(InputStream is) {
+ // Set up an ascii reader for the document (documents should not use
+ // other characters before encoding is defined)
+ Reader asciiReader = new ASCIIReader(is, MAX_OFFSET);
+ StreamTokenizer tokenizer = new StreamTokenizer(asciiReader);
+
+ // tokenizer.eolIsSignificant(false);// default false
+ // tokenizer.slashSlashComments(false); // default false
+ // tokenizer.slashStarComments(false);// default false
+ tokenizer.lowerCaseMode(false);
+
+ // tokenizer.quoteChar('\"'); // default quote char
+ tokenizer.ordinaryChar('\''); // default quote char
+ tokenizer.ordinaryChar('/'); // default comment character
+
+ String charset = getCharsetFromHTMLTokens(tokenizer);
+ if (asciiReader != null) {
+ try {
+ asciiReader.close();
+ } catch (IOException ioe) {
+ }
+ }
+ return charset;
+ }
+ public String getCharsetFromHTMLTokens(StreamTokenizer tokenizer) {
+ // keeps track of content attribute attribute until parsing
+ // of the meta tag is complete
+ String contentValue = null;
+
+ // initialize states
+ int stateContent = STATE_HTTP_START;
+ int stateElement = STATE_ELEMENT_START;
+ int stateHttp = STATE_HTTP_START;
+
+ try {
+ // in the worst case, process tokens until end of file
+ for (int token = tokenizer.nextToken();
+ token != StreamTokenizer.TT_EOF;
+ token = tokenizer.nextToken()) {
+ // debug tokens
+ // if (token == StreamTokenizer.TT_WORD) {
+ // System.out.println("word =" + tokenizer.sval);
+ // } else if (token == StreamTokenizer.TT_NUMBER) {
+ // System.out.println("number =" + tokenizer.nval);
+ // } else if (token == StreamTokenizer.TT_EOL) {
+ // System.out.println("endofline=");
+ // } else if ((char) token == '\"') {
+ // System.out.println("\" =" + tokenizer.sval);
+ //
+ // } else {
+ // System.out.println("else =" + (char) token);
+ // }
+
+ // process input based depending on current state
+ switch (stateElement) {
+ case STATE_ELEMENT_START :
+ if (token == '<') {
+ stateElement = STATE_ELEMENT_AFTER_LT;
+ } // else do nothing, cannot be beginning of META tag
+ break;
+ case STATE_ELEMENT_AFTER_LT :
+ if (token == StreamTokenizer.TT_WORD) {
+ // some element opened
+ if (ELEMENT_META
+ .equalsIgnoreCase(tokenizer.sval)) {
+ // META element opened
+ stateElement = STATE_ELEMENT_META;
+ // initialize state of attributes
+ stateHttp = STATE_HTTP_START;
+ stateContent = STATE_CONTENT_START;
+ contentValue = null;
+ } else if (
+ ELEMENT_BODY.equalsIgnoreCase(
+ tokenizer.sval)) {
+ // body element opened, we are too far, stop processing input
+ return null;
+ } else {
+ // some other element opened, start from initial state
+ stateElement = STATE_ELEMENT_START;
+ }
+ } else if (token == '/') {
+ // can be begging of head closing
+ stateElement = STATE_ELEMENT_AFTER_LT_SLASH;
+ } else {
+ // not an element opened, could be openning of declaration
+ // or element closing e.t.c.
+ stateElement = STATE_ELEMENT_START;
+ }
+ break;
+ case STATE_ELEMENT_AFTER_LT_SLASH :
+ if (token == StreamTokenizer.TT_WORD
+ && ELEMENT_HEAD.equalsIgnoreCase(tokenizer.sval)) {
+ // head element closed, we are too far, stop processing input
+ return null;
+ } else {
+ stateElement = STATE_ELEMENT_START;
+ }
+ break;
+ default : // STATE_META_IN :
+ switch (token) {
+ case '>' :
+ // no longer inside META, start from initial state
+ stateElement = STATE_ELEMENT_START;
+ break;
+ case StreamTokenizer.TT_WORD :
+ // string inside META tag, can be attribute name
+ if (ATTRIBUTE_HTTP
+ .equalsIgnoreCase(tokenizer.sval)) {
+ // found HTTP-EQUIV attribute name
+ stateHttp = STATE_HTTP_AFTER_NAME;
+ } else if (
+ ATTRIBUTE_CONTENT.equalsIgnoreCase(
+ tokenizer.sval)) {
+ // found CONTENT attribute name
+ stateContent = STATE_CONTENT_AFTER_NAME;
+ } else if (
+ stateHttp == STATE_HTTP_AFTER_EQ
+ && ATTRIBUTE_HTTP_VALUE.equalsIgnoreCase(
+ tokenizer.sval)) {
+ // value of HTTP-EQUIV attribute (unquoted)
+ // we found <META ... HTTP-EQUIV=content-type
+ stateHttp = STATE_HTTP_DONE;
+ } else {
+ // some other attribute name or string,
+ // reset states of seeked attributes,
+ // unless successfully processed earlier
+ if (stateHttp != STATE_HTTP_DONE) {
+ stateHttp = STATE_HTTP_START;
+ }
+ if (stateContent != STATE_CONTENT_DONE) {
+ stateContent = STATE_CONTENT_START;
+ }
+ }
+ break;
+ case '=' :
+ // = inside META tag, can separate interesing us
+ // attribute names from values
+ if (stateHttp == STATE_HTTP_AFTER_NAME) {
+ // we have HTTP-EQUIV=
+ stateHttp = STATE_HTTP_AFTER_EQ;
+ } else if (
+ stateContent == STATE_CONTENT_AFTER_NAME) {
+ // we have CONTENT=
+ stateContent = STATE_CONTENT_AFTER_EQ;
+ } else {
+ // equal sign after some other attribute name or string,
+ // reset states of seeked attributes,
+ // unless successfully processed earlier
+ if (stateHttp != STATE_HTTP_DONE) {
+ stateHttp = STATE_HTTP_START;
+ }
+ if (stateContent != STATE_CONTENT_DONE) {
+ stateContent = STATE_CONTENT_START;
+ }
+ }
+ break;
+ case '\"' :
+ // quoted string inside META tag, can be attribute value
+ if (stateHttp == STATE_HTTP_AFTER_EQ) {
+ // value of HTTP-EQUIV attribute
+ if (ATTRIBUTE_HTTP_VALUE
+ .equalsIgnoreCase(tokenizer.sval)) {
+ // we found <META ... HTTP-EQUIV="content-type"
+ stateHttp = STATE_HTTP_DONE;
+ }
+ } else if (
+ stateContent == STATE_CONTENT_AFTER_EQ) {
+ // value of CONTENT attribute
+ stateContent = STATE_CONTENT_DONE;
+ // save the value of the attribute
+ // if attribue HTTP-EQUIV="content-type" is found
+ // in the same META tag, this value might have
+ // Content-type entity header
+ contentValue = tokenizer.sval;
+ } else {
+ // value for the attribute is missing
+ // reset states of seeked attributes,
+ // unless successfully processed earlier
+ if (stateHttp != STATE_HTTP_DONE) {
+ stateHttp = STATE_HTTP_START;
+ }
+ if (stateContent != STATE_CONTENT_DONE) {
+ stateContent = STATE_CONTENT_START;
+ }
+ }
+ break;
+ default :
+ // other unexpected token inside META tag
+ // reset states of seeked attributes,
+ // unless successfully processed earlier
+ if (stateHttp != STATE_HTTP_DONE) {
+ stateHttp = STATE_HTTP_START;
+ }
+ if (stateContent != STATE_CONTENT_DONE) {
+ stateContent = STATE_CONTENT_START;
+ }
+ break;
+ }
+ break;
+ }
+ if (contentValue != null
+ && stateHttp == STATE_HTTP_DONE
+ && stateContent == STATE_CONTENT_DONE) {
+ // <META HTTP-EQUIV="content-type" CONTENT="*******"
+ // parse vale of content attribute to extract encoding
+ return getCharsetFromHTTP(contentValue);
+ }
+
+ }
+ } catch (IOException ioe) {
+ return null;
+ }
+ // end of file
+ return null;
+ }
+ /**
+ * Parses HTTP1.1 Content-Type entity-header field
+ * for example, Content-Type: text/html; charset=ISO-8859-4,
+ * and extracts charset parameter value of the media sub type.
+ * @param media-type, for example Content-Type: text/html; charset=ISO-8859-4
+ * @return value of charset parameter, for example ISO-8859-4
+ * or null if parameter does not exist
+ */
+ public String getCharsetFromHTTP(String contentValue) {
+ StringTokenizer t = new StringTokenizer(contentValue, ";");
+ while (t.hasMoreTokens()) {
+ String parameter = t.nextToken().trim();
+ if (parameter.startsWith("charset=")) {
+ String charset =
+ parameter.substring("charset=".length()).trim();
+ if (charset.length() > 0) {
+ return charset;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchHitCollector.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchHitCollector.java
new file mode 100644
index 000000000..10ee4cebf
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchHitCollector.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import org.apache.lucene.search.Hits;
+
+/**
+ * Search hit coollector. The search engine adds hits to it.
+ */
+public interface ISearchHitCollector {
+ /**
+ * Adds hits to the result
+ * @param Hits hits
+ */
+ public void addHits(Hits hits, String wordsSearched);
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchQuery.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchQuery.java
new file mode 100644
index 000000000..e30b1e894
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ISearchQuery.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.util.Collection;
+
+public interface ISearchQuery {
+ /**
+ * Obtains names of fields in addition to default field
+ */
+ public Collection getFieldNames();
+ /**
+ * Obtains search word (user query)
+ */
+ public String getSearchWord();
+ /**
+ * @return true if search only in specified fields, not the default field
+ */
+ public boolean isFieldSearch();
+ /**
+ * Obtains locale
+ */
+ public String getLocale();
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/IndexingOperation.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/IndexingOperation.java
new file mode 100644
index 000000000..179c84a11
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/IndexingOperation.java
@@ -0,0 +1,294 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.net.*;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.toc.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Indexing Operation represents a long operation,
+ * which performs indexing of the group (Collection) of documents.
+ * It is used Internally by SlowIndex and returned by its getIndexUpdateOperation() method.
+ */
+class IndexingOperation {
+ private int numAdded;
+ private int numRemoved;
+ private SearchIndex index = null;
+ // Constants for alocating progress among subtasks.
+ // The goal is to have a ratio among them
+ // that results in work accumulating at a constant rate.
+ private final static int WORK_PREPARE = 1; // * all documents
+ private final static int WORK_DELETEDOC = 5; // * removed documents
+ private final static int WORK_INDEXDOC = 50; // * added documents
+ private final static int WORK_SAVEINDEX = 2; // * all documents
+ private int workTotal;
+ /**
+ * Construct indexing operation.
+ * @param ix ISearchIndex already opened
+ * @param removedDocs collection of removed documents, including changed ones
+ * @param addedDocs collection of new documents, including changed ones
+ */
+ public IndexingOperation(SearchIndex ix) {
+ this.index = ix;
+ }
+
+ private void checkCancelled(IProgressMonitor pm)
+ throws OperationCanceledException {
+ if (pm.isCanceled())
+ throw new OperationCanceledException();
+ }
+ /**
+ * Executes indexing, given the progress monitor.
+ * @param monitor progres monitor to be used during this long operation
+ * for reporting progress
+ * @throws OperationCanceledException if indexing was cancelled
+ * @throws Exception if error occured
+ */
+ protected void execute(IProgressMonitor pm)
+ throws OperationCanceledException, IndexingException {
+ Collection removedDocs = getRemovedDocuments(index);
+ numRemoved = removedDocs.size();
+ Collection addedDocs = getAddedDocuments(index);
+ numAdded = addedDocs.size();
+
+ workTotal =
+ (numRemoved + numAdded) * WORK_PREPARE
+ + numAdded * WORK_INDEXDOC
+ + (numRemoved + numAdded) * WORK_SAVEINDEX;
+
+ if (numRemoved > 0) {
+ workTotal += (numRemoved + numAdded) * WORK_PREPARE
+ + numRemoved * WORK_DELETEDOC
+ + (numRemoved + numAdded) * WORK_SAVEINDEX;
+ }
+ // if collection is empty, we may return right away
+ // need to check if we have to do anything to the progress monitor
+ if (numRemoved + numAdded <= 0) {
+ pm.done();
+ return;
+ }
+
+ LazyProgressMonitor monitor = new LazyProgressMonitor(pm);
+ monitor.beginTask("", workTotal);
+ removeDocuments(monitor, removedDocs);
+ addDocuments(monitor, addedDocs);
+ monitor.done();
+ }
+
+ private void addDocuments(IProgressMonitor pm, Collection addedDocs)
+ throws IndexingException {
+ // Do not check here if (addedDocs.size() > 0), always perform add batch
+ // to ensure that index is created and saved even if no new documents exist
+
+ // now add all the new documents
+ if (!index.beginAddBatch()) {
+ throw new IndexingException();
+ }
+ try {
+ checkCancelled(pm);
+ pm.worked((numRemoved + numAdded) * WORK_PREPARE);
+ pm.subTask(Resources.getString("UpdatingIndex"));
+ for (Iterator it = addedDocs.iterator(); it.hasNext();) {
+ URL doc = (URL) it.next();
+ index.addDocument(getName(doc), doc);
+ checkCancelled(pm);
+ pm.worked(WORK_INDEXDOC);
+ }
+ } catch (OperationCanceledException oce) {
+ // Need to perform rollback on the index
+ pm.subTask(Resources.getString("Undoing_document_adds"));
+ pm.worked(workTotal);
+ // if (!index.abortUpdate())
+ // throw new Exception();
+ throw oce;
+ }
+ pm.subTask(Resources.getString("Writing_index"));
+ if (!index.endAddBatch())
+ throw new IndexingException();
+ }
+
+ private void removeDocuments(IProgressMonitor pm, Collection removedDocs)
+ throws IndexingException {
+
+ pm.subTask(Resources.getString("Preparing_for_indexing"));
+ checkCancelled(pm);
+
+ if (numRemoved > 0) {
+ if (!index.beginDeleteBatch())
+ throw new IndexingException();
+ try {
+ checkCancelled(pm);
+ pm.worked((numRemoved + numAdded) * WORK_PREPARE);
+ pm.subTask(Resources.getString("UpdatingIndex"));
+ for (Iterator it = removedDocs.iterator(); it.hasNext();) {
+ URL doc = (URL) it.next();
+ index.removeDocument(getName(doc));
+ checkCancelled(pm);
+ pm.worked(WORK_DELETEDOC);
+ }
+ } catch (OperationCanceledException oce) {
+ // Need to perform rollback on the index
+ pm.subTask(Resources.getString("Undoing_document_deletions"));
+ pm.worked(workTotal);
+ // if (!index.abortUpdate())
+ // throw new Exception();
+ throw oce;
+ }
+ if (!index.endDeleteBatch()) {
+ throw new IndexingException();
+ }
+ pm.worked((numRemoved + numAdded) * WORK_SAVEINDEX);
+ }
+ }
+ /**
+ * Returns the document identifier. Currently we use the
+ * document file name as identifier.
+ */
+ private String getName(URL doc) {
+ String name = doc.getFile();
+ // remove query string if any
+ int i = name.indexOf('?');
+ if (i != -1)
+ name = name.substring(0, i);
+ return name;
+ }
+ public class IndexingException extends Exception {
+ }
+ /**
+ * Returns the documents to be added to index.
+ * The collection consists of the associated PluginURL objects.
+ */
+ private Collection getAddedDocuments(SearchIndex index) {
+ // Get the list of added plugins
+ Collection addedPlugins = index.getDocPlugins().getAdded();
+ if (addedPlugins == null || addedPlugins.isEmpty())
+ return new ArrayList(0);
+ // get the list of all navigation urls.
+ Set urls = getAllDocuments(index.getLocale());
+ Set addedDocs = new HashSet(urls.size());
+ for (Iterator docs = urls.iterator(); docs.hasNext();) {
+ String doc = (String) docs.next();
+ // Assume the url is /pluginID/path_to_topic.html
+ int i = doc.indexOf('/', 1);
+ String plugin = i == -1 ? "" : doc.substring(1, i);
+ if (!addedPlugins.contains(plugin)) {
+ continue;
+ }
+
+ URL url = getIndexableURL(doc);
+ if (url != null) {
+ addedDocs.add(url);
+ }
+ }
+ return addedDocs;
+ }
+
+ /**
+ * Returns the documents to be removed from index.
+ * The collection consists of the associated PluginURL objects.
+ */
+ private Collection getRemovedDocuments(SearchIndex index) {
+ // Get the list of removed plugins
+ Collection removedPlugins = index.getDocPlugins().getRemoved();
+ if (removedPlugins == null || removedPlugins.isEmpty())
+ return new ArrayList(0);
+ // get the list of indexed docs. This is a hashtable (url, plugin)
+ HelpProperties indexedDocs = index.getIndexedDocs();
+ Set removedDocs = new HashSet(indexedDocs.size());
+ for (Iterator docs = indexedDocs.keySet().iterator();
+ docs.hasNext();
+ ) {
+ String doc = (String) docs.next();
+ // Assume the url is /pluginID/path_to_topic.html
+ int i = doc.indexOf('/', 1);
+ String plugin = i == -1 ? "" : doc.substring(1, i);
+ if (!removedPlugins.contains(plugin)) {
+ continue;
+ }
+
+ URL url = getIndexableURL(doc);
+ if (url != null) {
+ removedDocs.add(url);
+ }
+ }
+ return removedDocs;
+ }
+ /**
+ * Adds the topic and its subtopics to the list of documents
+ */
+ private void add(ITopic topic, Set hrefs) {
+ String href = topic.getHref();
+ if (href != null && !href.equals("") && !href.startsWith("http://"))
+ hrefs.add(href);
+ ITopic[] subtopics = topic.getSubtopics();
+ for (int i = 0; i < subtopics.length; i++)
+ add(subtopics[i], hrefs);
+ }
+ /**
+ * Returns the collection of href's for all the help topics.
+ */
+ private Set getAllDocuments(String locale) {
+ HashSet hrefs = new HashSet();
+ IToc[] tocs = HelpCore.getTocManager().getTocs(locale);
+ for (int i = 0; i < tocs.length; i++) {
+ ITopic[] topics = tocs[i].getTopics();
+ for (int j = 0; j < topics.length; j++) {
+ add(topics[j], hrefs);
+ }
+ if (tocs[i] instanceof Toc) {
+ topics = ((Toc) tocs[i]).getExtraTopics();
+ for (int j = 0; j < topics.length; j++) {
+ add(topics[j], hrefs);
+ }
+ }
+ ITopic tocDescriptionTopic = tocs[i].getTopic(null);
+ if (tocDescriptionTopic != null)
+ add(tocDescriptionTopic, hrefs);
+ }
+ return hrefs;
+ }
+ /**
+ * Checks if document is indexable, and creates
+ * a URL to obtain contents.
+ * @param url specified in the navigation
+ * @return URL to obtain document content or null
+ */
+ private URL getIndexableURL(String url) {
+ String fileName = url.toLowerCase();
+ if (fileName.endsWith(".htm")
+ || fileName.endsWith(".html")
+ || fileName.endsWith(".txt")
+ || fileName.endsWith(".xml")) {
+ // indexable
+ } else if (
+ fileName.indexOf(".htm#") >= 0
+ || fileName.indexOf(".html#") >= 0
+ || fileName.indexOf(".xml#") >= 0) {
+ url = url.substring(0, url.lastIndexOf('#'));
+ // its a fragment, index whole document
+ } else {
+ // not indexable
+ return null;
+ }
+
+ try {
+ return new URL("help:" + url + "?lang=" + index.getLocale());
+ } catch (MalformedURLException mue) {
+ return null;
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/LazyProgressMonitor.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/LazyProgressMonitor.java
new file mode 100644
index 000000000..8e277db13
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/LazyProgressMonitor.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+import org.eclipse.core.runtime.IProgressMonitor;
+import java.lang.String;
+import org.eclipse.core.runtime.ProgressMonitorWrapper;
+/**
+ * Progress Monitor, that accumulates work without
+ * communicating it immidiately to the underlying monitor.
+ * The work is sent in larger chunks for performance reasons.
+ */
+class LazyProgressMonitor extends ProgressMonitorWrapper {
+ // maximum number of times worked() should be called
+ // on underlying progress monitor
+ private static final int MAX_STEPS = 100;
+ private final IProgressMonitor monitor;
+ private int totalWork;
+ private int work;
+ private int lastWorked;
+ private int treshold;
+ protected LazyProgressMonitor(IProgressMonitor monitor) {
+ super(monitor);
+ this.monitor = monitor;
+ }
+ /**
+ * @see IProgressMonitor#beginTask
+ */
+ public void beginTask(String name, int totalWork) {
+ if (totalWork > 0) {
+ this.totalWork = totalWork;
+ }
+ monitor.beginTask(name, totalWork);
+ work = 0;
+ lastWorked = 0;
+ treshold = 1 + totalWork / MAX_STEPS;
+ }
+ /**
+ * @see IProgressMonitor#worked
+ */
+ public void worked(int newWork) {
+ this.work += newWork;
+ if (work >= treshold) {
+ monitor.worked(work - lastWorked);
+ lastWorked = work;
+ treshold = work + 1 + totalWork / MAX_STEPS;
+ }
+
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/ParsedDocument.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ParsedDocument.java
new file mode 100644
index 000000000..36c0391e3
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ParsedDocument.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.io.*;
+
+/**
+ * Parsed Document.
+ * It can be used to obtain multiple readers for the same document.
+ */
+public class ParsedDocument {
+ // Limit on how many characters will be indexed
+ // from a large document
+ private static final int charsLimit = 1000000;
+ Reader reader;
+ boolean read;
+ char[] docChars;
+
+ /**
+ * Constructor for ParsedDocument.
+ * @param reader reader obtained from the parser
+ */
+ public ParsedDocument(Reader reader) {
+ this.reader = reader;
+ this.read = false;
+ }
+ public Reader newContentReader() {
+ if (!read) {
+ read = true;
+ readDocument();
+ }
+ return new CharArrayReader(docChars);
+ }
+ private void readDocument() {
+ CharArrayWriter writer = new CharArrayWriter();
+ char[] buf = new char[4096];
+ int n;
+ int charsWritten = 0;
+ try {
+ while (0 <= (n = reader.read(buf))) {
+ if (charsWritten < charsLimit) {
+ if (n > charsLimit - charsWritten) {
+ // do not exceed the specified limit of characters
+ writer.write(buf, 0, charsLimit - charsWritten);
+ charsWritten = charsLimit;
+ } else {
+ writer.write(buf, 0, n);
+ charsWritten += n;
+ }
+ } else {
+ // do not break out of the loop
+ // keep reading to avoid breaking pipes
+ }
+ }
+ } catch (IOException ioe) {
+ // do not do anything, will use characters read so far
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException ioe2) {
+ }
+ }
+ docChars = writer.toCharArray();
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/ProgressDistributor.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ProgressDistributor.java
new file mode 100644
index 000000000..c15e53e3c
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/ProgressDistributor.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Distributes progress information from this monitor
+ * to multiple monitors.
+ */
+public class ProgressDistributor implements IProgressMonitor {
+ private int totalWork = -1;
+ private int worked = 0;
+ private boolean done = false;
+ String taskName;
+ String subTaskName;
+ /**
+ * Map work indexed by montitor
+ */
+ private Collection monitors = new ArrayList();
+
+ /**
+ * @see IProgressMonitor#beginTask(String, int)
+ */
+ public synchronized void beginTask(String name, int totalWork) {
+ this.totalWork = totalWork;
+ this.worked=0;
+ this.done=false;
+ for (Iterator it = monitors.iterator(); it.hasNext();) {
+ IProgressMonitor m = (IProgressMonitor) it.next();
+ m.beginTask(name, totalWork);
+ }
+ }
+
+ /**
+ * @see IProgressMonitor#done()
+ */
+ public synchronized void done() {
+ done = true;
+ for (Iterator it = monitors.iterator(); it.hasNext();) {
+ IProgressMonitor m = (IProgressMonitor) it.next();
+ m.done();
+ }
+ }
+
+ /**
+ * @see IProgressMonitor#internalWorked(double)
+ */
+ public void internalWorked(double work) {
+ }
+
+ /**
+ * @see IProgressMonitor#isCanceled()
+ */
+ public synchronized boolean isCanceled() {
+ for (Iterator it = monitors.iterator(); it.hasNext();) {
+ IProgressMonitor m = (IProgressMonitor) it.next();
+ if(m.isCanceled()){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @see IProgressMonitor#setCanceled(boolean)
+ */
+ public void setCanceled(boolean value) {
+ }
+
+ /**
+ * @see IProgressMonitor#setTaskName(String)
+ */
+ public synchronized void setTaskName(String name) {
+ taskName = name;
+ for (Iterator it = monitors.iterator(); it.hasNext();) {
+ IProgressMonitor m = (IProgressMonitor) it.next();
+ m.setTaskName(name);
+ }
+ }
+
+ /**
+ * @see IProgressMonitor#subTask(String)
+ */
+ public synchronized void subTask(String name) {
+ subTaskName = name;
+ for (Iterator it = monitors.iterator(); it.hasNext();) {
+ IProgressMonitor m = (IProgressMonitor) it.next();
+ m.subTask(name);
+ }
+ }
+
+ /**
+ * @see IProgressMonitor#worked(int)
+ */
+ public synchronized void worked(int work) {
+ worked += work;
+ for (Iterator it = monitors.iterator(); it.hasNext();) {
+ IProgressMonitor m = (IProgressMonitor) it.next();
+ m.worked(work);
+ }
+ }
+ public synchronized void addMonitor(IProgressMonitor m) {
+ if (totalWork > -1)
+ m.beginTask(taskName, totalWork);
+ if (subTaskName != null)
+ m.subTask(subTaskName);
+ if (worked > 0)
+ m.worked(worked);
+ if (done)
+ m.done();
+ monitors.add(m);
+ }
+ public synchronized void removeMonitor(IProgressMonitor m) {
+ monitors.remove(m);
+ }
+ public synchronized void operationCanceled(){
+ totalWork = -1;
+ worked = 0;
+ done = false;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryBuilder.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryBuilder.java
new file mode 100644
index 000000000..0d23af164
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryBuilder.java
@@ -0,0 +1,411 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+import java.io.*;
+import java.util.*;
+
+import org.apache.lucene.analysis.*;
+import org.apache.lucene.index.*;
+import org.apache.lucene.search.*;
+import org.eclipse.help.internal.*;
+/**
+ * Build query acceptable by the search engine.
+ */
+public class QueryBuilder {
+
+ // Descriptor of Analyzer to process the query words
+ private AnalyzerDescriptor analyzerDesc;
+ // Analyzer to process the query words
+ private Analyzer analyzer;
+
+ // List of QueryWordsToken
+ private List analyzedTokens;
+
+ // List of words to highlight
+ private List highlightWords = new ArrayList();
+
+ private Locale locale;
+
+ /**
+ * Creates a query builder for the search word. The search word is processed
+ * by a lexical analyzer.
+ */
+ public QueryBuilder(String searchWords, AnalyzerDescriptor analyzerDesc) {
+ String language = analyzerDesc.getLang();
+ if (language.length() >= 5) {
+ this.locale =
+ new Locale(language.substring(0, 2), language.substring(3, 5));
+ } else {
+ this.locale = new Locale(language.substring(0, 2), "");
+ }
+ this.analyzerDesc = analyzerDesc;
+ this.analyzer = analyzerDesc.getAnalyzer();
+ // split search query into tokens
+ List userTokens = tokenizeUserQuery(searchWords);
+ analyzedTokens = analyzeTokens(userTokens);
+ }
+ /**
+ * Splits user query into tokens and returns a list of QueryWordsToken's.
+ */
+ private List tokenizeUserQuery(String searchWords) {
+ List tokenList = new ArrayList();
+ //Divide along quotation marks
+ StringTokenizer qTokenizer =
+ new StringTokenizer(searchWords.trim(), "\"", true);
+ boolean withinQuotation = false;
+ String quotedString = "";
+ while (qTokenizer.hasMoreTokens()) {
+ String curToken = qTokenizer.nextToken();
+ if (curToken.equals("\"")) {
+ if (withinQuotation) {
+ tokenList.add(QueryWordsToken.exactPhrase(quotedString));
+ } else {
+ quotedString = "";
+ }
+ withinQuotation = !withinQuotation;
+ continue;
+ } else if (withinQuotation) {
+ quotedString = curToken;
+ continue;
+ } else {
+ //divide unquoted strings along white space
+ StringTokenizer parser = new StringTokenizer(curToken.trim());
+ while (parser.hasMoreTokens()) {
+ String token = parser.nextToken();
+ if (token.equalsIgnoreCase(QueryWordsToken.AND().value))
+ tokenList.add(QueryWordsToken.AND());
+ else if (
+ token.equalsIgnoreCase(QueryWordsToken.OR().value))
+ tokenList.add(QueryWordsToken.OR());
+ else if (
+ token.equalsIgnoreCase(QueryWordsToken.NOT().value))
+ tokenList.add(QueryWordsToken.NOT());
+ else
+ tokenList.add(QueryWordsToken.word(token));
+ }
+ }
+ }
+ return tokenList;
+ }
+ /**
+ * Apply the Analyzer to the search tokens and return the list of processed QueryWordsToken's.
+ */
+ private List analyzeTokens(List tokens) {
+ List newTokens = new ArrayList();
+ for (int i = 0; i < tokens.size(); i++) {
+ QueryWordsToken token = (QueryWordsToken) tokens.get(i);
+ if (token.type == QueryWordsToken.WORD) {
+ int questionMIndex = token.value.indexOf('?');
+ int starIndex = token.value.indexOf('*');
+ if (starIndex >= 0 || questionMIndex >= 0) {
+ if (questionMIndex != 0 && starIndex != 0) {
+ newTokens.add(
+ QueryWordsToken.word(
+ token.value.toLowerCase(locale)));
+
+ // add word to the list of words to highlight
+ if (!highlightWords.contains(token.value)) {
+ highlightWords.add(token.value);
+ }
+ } else {
+ // wild card not allowed as the first character
+ }
+ } else {
+ List wordList =
+ analyzeText(analyzer, "contents", token.value);
+
+ if (wordList.size() > 0) {
+ if (!highlightWords.contains(token.value)) {
+ // add original word to the list of words to highlight
+ highlightWords.add(token.value);
+ }
+
+ if (wordList.size() == 1) {
+ String word = (String) wordList.get(0);
+ newTokens.add(QueryWordsToken.word(word));
+
+ // add analyzed word to the list of words to highlight
+ // this is required to highlight stemmed words
+ if (!highlightWords.contains(word)) {
+ highlightWords.add(word);
+ }
+ } else {
+ QueryWordsPhrase phrase = QueryWordsToken.phrase();
+ for (Iterator it = wordList.iterator();
+ it.hasNext();
+ ) {
+ String word = (String) it.next();
+ phrase.addWord(word);
+
+ // add each analyzed word to the list of words to highlight
+ // this is only required to highlight stemmed words.
+ // Adding words should not be done when DefaultAnalyzer is used,
+ // because it does not perform stemming and common words removal
+ // which would result in common characters highlighted all over (bug 30263)
+ if (!analyzerDesc
+ .getId()
+ .startsWith(HelpBasePlugin.PLUGIN_ID + "#")) {
+ if (!highlightWords.contains(word)) {
+ highlightWords.add(word);
+ }
+ }
+ }
+ newTokens.add(phrase);
+ }
+ }
+
+ }
+ } else if (// forget ANDs
+ /*token.type == SearchQueryToken.AND
+ ||*/
+ token.type == QueryWordsToken.OR
+ || token.type == QueryWordsToken.NOT)
+ newTokens.add(token);
+ else if (token.type == QueryWordsToken.EXACT_PHRASE) {
+ List wordList =
+ analyzeText(analyzer, "exact_contents", token.value);
+
+ if (wordList.size() > 0) {
+ if (!highlightWords.contains(token.value)) {
+ // add original word to the list of words to highlight
+ highlightWords.add(token.value);
+ }
+ }
+
+ QueryWordsExactPhrase phrase = QueryWordsToken.exactPhrase();
+ for (Iterator it = wordList.iterator(); it.hasNext();) {
+ String word = (String) it.next();
+ phrase.addWord(word);
+
+ // add analyzed word to the list of words to highlight
+ // if (!highlightWords.contains(word))
+ // highlightWords.add(word);
+ }
+ // add phrase only if not empty
+ if (phrase.getWords().size() > 0) {
+ newTokens.add(phrase);
+ }
+ }
+ }
+ return newTokens;
+ }
+ /**
+ * Get a list of tokens corresponding to a search word or phrase
+ * @return List of String
+ */
+ private List analyzeText(
+ Analyzer analyzer,
+ String fieldName,
+ String text) {
+ List words = new ArrayList(1);
+ Reader reader = new StringReader(text);
+ TokenStream tStream = analyzer.tokenStream(fieldName, reader);
+ Token tok;
+ try {
+ while (null != (tok = tStream.next())) {
+ words.add(tok.termText());
+ }
+ reader.close();
+ } catch (IOException ioe) {
+ }
+ return words;
+ }
+ /**
+ * Obtains Lucene Query from tokens
+ * @return Query or null if no query could be created
+ */
+ private Query createLuceneQuery(
+ List searchTokens,
+ String[] fieldNames,
+ float[] boosts) {
+ // Get queries for parts separated by OR
+ List requiredQueries =
+ getRequiredQueries(searchTokens, fieldNames, boosts);
+ if (requiredQueries.size() == 0)
+ return null;
+ else if (requiredQueries.size() <= 1)
+ return (Query) requiredQueries.get(0);
+ else /*if (requiredQueries.size() > 1) */
+ // OR queries
+ return (orQueries(requiredQueries));
+ }
+ /**
+ * Obtains Lucene queries for token sequences separated at OR.
+ * @return List of Query (could be empty)
+ */
+ private List getRequiredQueries(
+ List tokens,
+ String[] fieldNames,
+ float[] boosts) {
+ List oredQueries = new ArrayList();
+ ArrayList requiredQueryTokens = new ArrayList();
+ for (int i = 0; i < tokens.size(); i++) {
+ QueryWordsToken token = (QueryWordsToken) tokens.get(i);
+ if (token.type != QueryWordsToken.OR) {
+ requiredQueryTokens.add(token);
+ } else {
+ Query reqQuery =
+ getRequiredQuery(requiredQueryTokens, fieldNames, boosts);
+ if (reqQuery != null)
+ oredQueries.add(reqQuery);
+ requiredQueryTokens = new ArrayList();
+ }
+ }
+ Query reqQuery =
+ getRequiredQuery(requiredQueryTokens, fieldNames, boosts);
+ if (reqQuery != null)
+ oredQueries.add(reqQuery);
+ return oredQueries;
+ }
+ private Query orQueries(Collection queries) {
+ BooleanQuery bq = new BooleanQuery();
+ for (Iterator it = queries.iterator(); it.hasNext();) {
+ Query q = (Query) it.next();
+ bq.add(q, false, false);
+ }
+ return bq;
+ }
+ /**
+ * Obtains Lucene Query for tokens containing only AND and NOT operators.
+ * @return BooleanQuery or null if no query could be created from the tokens
+ */
+ private Query getRequiredQuery(
+ List requiredTokens,
+ String[] fieldNames,
+ float[] boosts) {
+ BooleanQuery retQuery = new BooleanQuery();
+ boolean requiredTermExist = false;
+ // Parse tokens left to right
+ QueryWordsToken operator = null;
+ for (int i = 0; i < requiredTokens.size(); i++) {
+ QueryWordsToken token = (QueryWordsToken) requiredTokens.get(i);
+ if (token.type == QueryWordsToken.AND
+ || token.type == QueryWordsToken.NOT) {
+ operator = token;
+ continue;
+ }
+ // Creates queries for all fields
+ Query qs[] = new Query[fieldNames.length];
+ for (int f = 0; f < fieldNames.length; f++) {
+ qs[f] = token.createLuceneQuery(fieldNames[f], boosts[f]);
+ }
+ // creates the boolean query of all fields
+ Query q = qs[0];
+ if (fieldNames.length > 1) {
+ BooleanQuery allFieldsQuery = new BooleanQuery();
+ for (int f = 0; f < fieldNames.length; f++)
+ allFieldsQuery.add(qs[f], false, false);
+ q = allFieldsQuery;
+ }
+ if (operator != null && operator.type == QueryWordsToken.NOT) {
+ retQuery.add(q, false, true); // add as prohibited
+ } else {
+ retQuery.add(q, true, false); // add as required
+ requiredTermExist = true;
+ }
+ }
+ if (!requiredTermExist) {
+ return null; // cannot search for prohibited only
+ }
+ return retQuery;
+ }
+ private Query getLuceneQuery(String[] fieldNames, float[] boosts) {
+ Query luceneQuery =
+ createLuceneQuery(analyzedTokens, fieldNames, boosts);
+ return luceneQuery;
+ }
+ /**
+ * @param fieldNames - Collection of field names of type String (e.g. "h1");
+ * the search will be performed on the given fields
+ * @param fieldSearch - boolean indicating if field only search
+ * should be performed; if set to false, default field "contents"
+ * and all other fields will be searched
+ */
+ public Query getLuceneQuery(
+ Collection fieldNames,
+ boolean fieldSearchOnly) {
+ String[] fields;
+ float[] boosts;
+ if (fieldSearchOnly) {
+ fields = new String[fieldNames.size()];
+ boosts = new float[fieldNames.size()];
+ Iterator fieldNamesIt = fieldNames.iterator();
+ for (int i = 0; i < fieldNames.size(); i++) {
+ fields[i] = (String) fieldNamesIt.next();
+ boosts[i] = 5.0f;
+ }
+ } else {
+ fields = new String[fieldNames.size() + 1];
+ boosts = new float[fieldNames.size() + 1];
+ Iterator fieldNamesIt = fieldNames.iterator();
+ for (int i = 0; i < fieldNames.size(); i++) {
+ fields[i] = (String) fieldNamesIt.next();
+ boosts[i] = 5.0f;
+ }
+ fields[fieldNames.size()] = "contents";
+ boosts[fieldNames.size()] = 1.0f;
+ }
+ Query query = getLuceneQuery(fields, boosts);
+ query = improveRankingForUnqotedPhrase(query, fields, boosts);
+ return query;
+ }
+ /**
+ * If user query contained only words (no quotaions nor operators)
+ * extends query with term phrase representing entire user query
+ * i.e for user string a b, the query a AND b will be extended
+ * to "a b" OR a AND b
+ */
+ private Query improveRankingForUnqotedPhrase(
+ Query query,
+ String[] fields,
+ float[] boosts) {
+ if (query == null)
+ return query;
+ // check if all tokens are words
+ for (int i = 0; i < analyzedTokens.size(); i++)
+ if (((QueryWordsToken) analyzedTokens.get(i)).type
+ != QueryWordsToken.WORD)
+ return query;
+ // Create phrase query for all tokens and OR with original query
+ BooleanQuery booleanQuery = new BooleanQuery();
+ booleanQuery.add(query, false, false);
+ PhraseQuery[] phraseQueries = new PhraseQuery[fields.length];
+ for (int f = 0; f < fields.length; f++) {
+ phraseQueries[f] = new PhraseQuery();
+ for (int i = 0; i < analyzedTokens.size(); i++) {
+ Term t =
+ new Term(
+ fields[f],
+ ((QueryWordsToken) analyzedTokens.get(i)).value);
+ phraseQueries[f].add(t);
+ }
+ phraseQueries[f].setBoost(10 * boosts[f]);
+ booleanQuery.add(phraseQueries[f], false, false);
+ }
+ return booleanQuery;
+ }
+ /**
+ * 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 gethighlightTerms() {
+ StringBuffer buf = new StringBuffer();
+ for (Iterator it = highlightWords.iterator(); it.hasNext();) {
+ buf.append('"');
+ buf.append(it.next());
+ buf.append("\" ");
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsExactPhrase.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsExactPhrase.java
new file mode 100644
index 000000000..a9ac202f1
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsExactPhrase.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+import java.util.*;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.*;
+/**
+ * Represents a quoted token in user search query words
+ */
+public class QueryWordsExactPhrase extends QueryWordsToken {
+ private List words;
+ public QueryWordsExactPhrase() {
+ super(QueryWordsToken.EXACT_PHRASE, "");
+ words = new ArrayList();
+ }
+ public void addWord(String word) {
+ words.add(word);
+ if (words.size() <= 1)
+ value = word;
+ else
+ value += " " + word;
+ }
+ public List getWords() {
+ return words;
+ }
+ /**
+ * Creates a lucene query for a field
+ */
+ public Query createLuceneQuery(String field, float boost)
+ {
+ PhraseQuery q = new PhraseQuery();
+ for (Iterator it = getWords().iterator(); it.hasNext();)
+ {
+ String word = (String) it.next();
+ Term t = new Term("exact_"+field, word);
+ q.add(t);
+ q.setBoost(boost);
+ }
+ return q;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsPhrase.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsPhrase.java
new file mode 100644
index 000000000..476ca5eb1
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsPhrase.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+import java.util.*;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.*;
+/**
+ * Represents a phrase (not quoted) token in user search query words
+ * It consists of several words created by an analyzer
+ */
+public class QueryWordsPhrase extends QueryWordsToken {
+ private List words;
+ public QueryWordsPhrase() {
+ super(QueryWordsToken.PHRASE, "");
+ words = new ArrayList();
+ }
+ public void addWord(String word) {
+ words.add(word);
+ if (words.size() <= 1)
+ value = word;
+ else
+ value += " " + word;
+ }
+ public List getWords() {
+ return words;
+ }
+ /**
+ * Creates a lucene query for a field
+ */
+ public Query createLuceneQuery(String field, float boost)
+ {
+ PhraseQuery q = new PhraseQuery();
+ for (Iterator it = getWords().iterator(); it.hasNext();)
+ {
+ String word = (String) it.next();
+ Term t = new Term(field, word);
+ q.add(t);
+ q.setBoost(boost);
+ }
+ return q;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsToken.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsToken.java
new file mode 100644
index 000000000..4e52f48b7
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/QueryWordsToken.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.*;
+/**
+ * Represents a token in user search query words
+ */
+public class QueryWordsToken {
+ public static final int AND = 0;
+ public static final int OR = 1;
+ public static final int NOT = 2;
+ public static final int EXACT_PHRASE = 3;
+ public static final int PHRASE = 4;
+ public static final int WORD = 5;
+ private static final QueryWordsToken fAND = new QueryWordsToken(AND, "AND");
+ private static final QueryWordsToken fOR = new QueryWordsToken(OR, "OR");
+ private static final QueryWordsToken fNOT = new QueryWordsToken(NOT, "NOT");
+ public int type;
+ public String value;
+ protected QueryWordsToken(int type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+ /**
+ * Creates a lucene query for a field
+ */
+ public Query createLuceneQuery(String field, float boost) {
+ 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);
+ }
+ // after updating Lucene, set boost on a Query class
+ return q;
+ }
+ public static QueryWordsToken AND() {
+ return fAND;
+ }
+ public static QueryWordsToken OR() {
+ return fOR;
+ }
+ public static QueryWordsToken NOT() {
+ return fNOT;
+ }
+ public static QueryWordsToken word(String word) {
+ return new QueryWordsToken(QueryWordsToken.WORD, word);
+ }
+ public static QueryWordsPhrase phrase() {
+ return new QueryWordsPhrase();
+ }
+ public static QueryWordsExactPhrase exactPhrase() {
+ return new QueryWordsExactPhrase();
+ }
+ public static QueryWordsExactPhrase exactPhrase(String word) {
+ QueryWordsExactPhrase token = new QueryWordsExactPhrase();
+ token.addWord(word);
+ return token;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchHit.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchHit.java
new file mode 100644
index 000000000..be59f8dd0
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchHit.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import org.eclipse.help.IToc;
+
+/**
+ * Search hit.
+ */
+public class SearchHit {
+ private String href;
+ private String label;
+ private float score;
+ private IToc toc;
+ /**
+ * Constructor
+ * @param toc TOC containing topic or null
+ */
+ public SearchHit(String href, String label, float score, IToc toc) {
+ this.href = href;
+ this.label = label;
+ this.score = score;
+ this.toc = toc;
+ }
+ /**
+ * Gets the href.
+ * @return Returns a String
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Gets the label.
+ * @return Returns a String
+ */
+ public String getLabel() {
+ return label;
+ }
+ /**
+ * Gets the score.
+ * @return Returns a float
+ */
+ public float getScore() {
+ return score;
+ }
+
+ /**
+ * Gets the toc.
+ * @return Returns IToc or null
+ */
+ public IToc getToc() {
+ return toc;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public void setScore(float score) {
+ this.score = score;
+ }
+
+ public void setToc(IToc toc) {
+ this.toc = toc;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java
new file mode 100644
index 000000000..d387cc1dd
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java
@@ -0,0 +1,487 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.zip.*;
+
+import org.apache.lucene.document.*;
+import org.apache.lucene.index.*;
+import org.apache.lucene.search.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Text search index. Documents added to this index
+ * can than be searched against a search query.
+ */
+public class SearchIndex {
+ private IndexReader ir;
+ private IndexWriter iw;
+ private File indexDir;
+ private String locale;
+ private AnalyzerDescriptor analyzerDescriptor;
+ private PluginVersionInfo docPlugins;
+ private String indexedDocsFile;
+ // table of all document names, used during indexing batches
+ private HelpProperties indexedDocs;
+ private static final String INDEXED_CONTRIBUTION_INFO_FILE =
+ "indexed_contributions";
+ public static final String INDEXED_DOCS_FILE = "indexed_docs";
+ private static final String ANALYZER_VERSION_FILENAME = "indexed_analyzer";
+ private File analyzerVersionFile;
+ private File inconsistencyFile;
+ private HTMLDocParser parser;
+ private IndexSearcher searcher;
+ /**
+ * Constructor.
+ * @param locale the locale this index uses
+ * @param analyzerDesc the analyzer used to index
+ */
+ public SearchIndex(String locale, AnalyzerDescriptor analyzerDesc) {
+ this(
+ locale,
+ analyzerDesc,
+ new File(
+ HelpBasePlugin.getDefault().getStateLocation().toOSString()
+ + File.separator
+ + "nl"
+ + File.separator
+ + locale));
+ }
+ /**
+ * Constructor.
+ * @param locale the locale this index uses
+ * @param analyzerDesc the analyzer used to index
+ */
+ public SearchIndex(
+ String locale,
+ AnalyzerDescriptor analyzerDesc,
+ File indexDir) {
+ super();
+ this.locale = locale;
+ this.indexDir = indexDir;
+ this.analyzerDescriptor = analyzerDesc;
+ inconsistencyFile =
+ new File(indexDir.getParentFile(), locale + ".inconsistent");
+ analyzerVersionFile = new File(indexDir, ANALYZER_VERSION_FILENAME);
+ indexedDocsFile =
+ "nl" + File.separator + locale + File.separator + INDEXED_DOCS_FILE;
+ parser = new HTMLDocParser();
+ if (!exists()) {
+ unzipProductIndex();
+ }
+ }
+ /**
+ * Indexes one document from a stream.
+ * Index has to be open and close outside of this method
+ * @param name the document identifier (could be a URL)
+ * @param url the URL of the document
+ * @return true if success
+ */
+ public boolean addDocument(String name, URL url) {
+ if (HelpBasePlugin.DEBUG_SEARCH) {
+ System.out.println(
+ "SearchIndex.addDocument(" + name + ", " + url + ")");
+ }
+ try {
+ Document doc = new Document();
+ doc.add(Field.Keyword("name", name));
+
+ try {
+ try {
+ parser.openDocument(url);
+ } catch (IOException ioe) {
+ HelpBasePlugin.logError(
+ Resources.getString("ES25", name),
+ null);
+ return false;
+ }
+ ParsedDocument parsed =
+ new ParsedDocument(parser.getContentReader());
+
+ doc.add(Field.Text("contents", parsed.newContentReader()));
+ doc.add(
+ Field.Text("exact_contents", parsed.newContentReader()));
+
+ String title = parser.getTitle();
+ 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);
+ } finally {
+ parser.closeDocument();
+ }
+ indexedDocs.put(name, "0");
+ return true;
+ } catch (IOException e) {
+ HelpBasePlugin.logError(
+ Resources.getString("ES16", name, indexDir.getAbsolutePath()),
+ e);
+ return false;
+ }
+ }
+
+ /**
+ * Starts additions.
+ * To be called before adding documents.
+ */
+ public boolean beginAddBatch() {
+ try {
+ if (iw != null) {
+ iw.close();
+ }
+ boolean create = false;
+ if (!exists()) {
+ create = true;
+ indexDir.mkdirs();
+ if (!indexDir.exists())
+ return false; // unable to setup index directory
+ }
+ indexedDocs =
+ new HelpProperties(indexedDocsFile, HelpBasePlugin.getDefault());
+ indexedDocs.restore();
+ setInconsistent(true);
+ iw =
+ new IndexWriter(
+ indexDir,
+ analyzerDescriptor.getAnalyzer(),
+ create);
+ iw.mergeFactor = 20;
+ iw.maxFieldLength = 1000000;
+ return true;
+ } catch (IOException e) {
+ HelpBasePlugin.logError(Resources.getString("ES17"), e);
+ return false;
+ }
+ }
+ /**
+ * Starts deletions.
+ * To be called before deleting documents.
+ */
+ public boolean beginDeleteBatch() {
+ try {
+ if (ir != null) {
+ ir.close();
+ }
+ indexedDocs =
+ new HelpProperties(indexedDocsFile, HelpBasePlugin.getDefault());
+ indexedDocs.restore();
+ setInconsistent(true);
+ ir = IndexReader.open(indexDir);
+ return true;
+ } catch (IOException e) {
+ HelpBasePlugin.logError(Resources.getString("ES18"), e);
+ return false;
+ }
+ }
+ /**
+ * Deletes a single document from the index.
+ * @param name - document name
+ * @return true if success
+ */
+ public boolean removeDocument(String name) {
+ if (HelpBasePlugin.DEBUG_SEARCH) {
+ System.out.println("SearchIndex.removeDocument(" + name + ")");
+ }
+ Term term = new Term("name", name);
+ try {
+ ir.delete(term);
+ indexedDocs.remove(name);
+ } catch (IOException e) {
+ HelpBasePlugin.logError(
+ Resources.getString("ES22", name, indexDir.getAbsolutePath()),
+ e);
+ return false;
+ }
+ return true;
+ }
+ /**
+ * Finish additions.
+ * To be called after adding documents.
+ */
+ public boolean endAddBatch() {
+ try {
+ if (iw == null)
+ return false;
+ iw.optimize();
+ iw.close();
+ // save the update info:
+ // - all the docs
+ // - plugins (and their version) that were indexed
+ indexedDocs.save();
+ indexedDocs = null;
+ getDocPlugins().save();
+ saveAnalyzerId();
+ setInconsistent(false);
+ return true;
+ } catch (IOException e) {
+ HelpBasePlugin.logError(Resources.getString("ES19"), e);
+ return false;
+ }
+ }
+ /**
+ * Finish deletions.
+ * To be called after deleting documents.
+ */
+ public boolean endDeleteBatch() {
+ try {
+ if (ir == null)
+ return false;
+ ir.close();
+ // save the update info:
+ // - all the docs
+ // - plugins (and their version) that were indexed
+ indexedDocs.save();
+ indexedDocs = null;
+ getDocPlugins().save();
+ saveAnalyzerId();
+ setInconsistent(false);
+ return true;
+ } catch (IOException e) {
+ HelpBasePlugin.logError(Resources.getString("ES20"), e);
+ return false;
+ }
+ }
+ /**
+ * Checks if index exists and is usable.
+ * @return true if index exists
+ */
+ public boolean exists() {
+ return indexDir.exists() && !isInconsistent();
+ // assume index exists if directory does
+ }
+ /**
+ * Performs a query search on this index
+ * @param fieldNames - Collection of field names of type String (e.g. "h1");
+ * the search will be performed on the given fields
+ * @param fieldSearch - boolean indicating if field only search
+ * should be performed; if set to false, default field "contents"
+ * and all other fields will be searched
+ * @param searchResult SearchResult that will contain all the hits
+ * @return - an array of document ids.
+ * Later, we can extend this to return more data (rank, # of occs, etc.)
+ */
+ public void search(
+ ISearchQuery searchQuery,
+ ISearchHitCollector collector) {
+ try {
+ QueryBuilder queryBuilder =
+ new QueryBuilder(
+ searchQuery.getSearchWord(),
+ analyzerDescriptor);
+ Query luceneQuery =
+ queryBuilder.getLuceneQuery(
+ searchQuery.getFieldNames(),
+ searchQuery.isFieldSearch());
+ String highlightTerms = queryBuilder.gethighlightTerms();
+ if (luceneQuery != null) {
+ if (searcher == null) {
+ openSearcher();
+ }
+ Hits hits = searcher.search(luceneQuery);
+ collector.addHits(hits, highlightTerms);
+ }
+ } catch (Exception e) {
+ HelpBasePlugin.logError(
+ Resources.getString("ES21", searchQuery.getSearchWord()),
+ e);
+ }
+ }
+ public String getLocale() {
+ return locale;
+ }
+ /**
+ * Returns the list of all the plugins in this session
+ * that have declared a help contribution.
+ */
+ public PluginVersionInfo getDocPlugins() {
+ if (docPlugins == null) {
+ Iterator docPluginsIterator =
+ HelpCore.getTocManager().getContributingPlugins().iterator();
+ docPlugins =
+ new PluginVersionInfo(
+ "nl"
+ + File.separator
+ + locale
+ + File.separator
+ + INDEXED_CONTRIBUTION_INFO_FILE,
+ docPluginsIterator,
+ HelpBasePlugin.getDefault(),
+ !exists());
+ }
+ return docPlugins;
+ }
+ /**
+ * We use HelpProperties, but a list would suffice.
+ * We only need the key values.
+ * @return HelpProperties, keys are URLs of indexed documents
+ */
+ public HelpProperties getIndexedDocs() {
+ HelpProperties indexedDocs =
+ new HelpProperties(indexedDocsFile, HelpBasePlugin.getDefault());
+ if (exists())
+ indexedDocs.restore();
+ return indexedDocs;
+ }
+ /**
+ * Gets analyzer identifier from a file.
+ */
+ private String readAnalyzerId() {
+ if (!analyzerVersionFile.exists())
+ return "";
+ try {
+ DataInputStream dis =
+ new DataInputStream(new FileInputStream(analyzerVersionFile));
+ String id = dis.readUTF();
+ dis.close();
+ return id;
+ } catch (IOException ioe) {
+ }
+ return "";
+ }
+ /**
+ * Saves analyzer identifier to a file.
+ */
+ private void saveAnalyzerId() {
+ try {
+ DataOutputStream dos =
+ new DataOutputStream(new FileOutputStream(analyzerVersionFile));
+ dos.writeUTF(analyzerDescriptor.getId());
+ dos.flush();
+ dos.close();
+ } catch (IOException ioe) {
+ }
+ }
+ /**
+ * @return Returns true if index has been left in inconsistent state
+ * If analyzer has changed to incompatible one,
+ * index is treated as inconsistent as well.
+ */
+ public boolean isInconsistent() {
+ if (inconsistencyFile.exists()) {
+ return true;
+ }
+ return !analyzerDescriptor.isCompatible(readAnalyzerId());
+ }
+ /**
+ * Writes or deletes inconsistency flag file
+ */
+ public void setInconsistent(boolean inconsistent) {
+ if (inconsistent) {
+ try {
+ // parent directory already created by beginAddBatch on new index
+ FileOutputStream fos = new FileOutputStream(inconsistencyFile);
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ } else
+ inconsistencyFile.delete();
+ }
+
+ public synchronized void openSearcher() throws IOException {
+ if (searcher == null) {
+ searcher = new IndexSearcher(indexDir.getAbsolutePath());
+ }
+ }
+ /**
+ * Closes IndexReader used by Searcher.
+ * Should be called on platform shutdown,
+ * when no more reading from index is performed.
+ */
+ public void close() {
+ if (searcher != null) {
+ try {
+ searcher.close();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+ /**
+ * Returns the indexDir.
+ * @return File
+ */
+ public File getIndexDir() {
+ return indexDir;
+ }
+ /**
+ * Finds and unzips prebuild index specified in preferences
+ */
+ private void unzipProductIndex() {
+ String indexPluginId =
+ HelpBasePlugin.getDefault().getPluginPreferences().getString(
+ "productIndex");
+ if (indexPluginId == null || indexPluginId.length() <= 0) {
+ return;
+ }
+ InputStream zipIn =
+ ResourceLocator.openFromPlugin(
+ indexPluginId,
+ "doc_index.zip",
+ getLocale());
+ if (zipIn == null) {
+ return;
+ }
+ byte[] buf = new byte[8192];
+ File destDir = getIndexDir();
+ ZipInputStream zis = new ZipInputStream(zipIn);
+ FileOutputStream fos = null;
+ try {
+ ZipEntry zEntry;
+ while ((zEntry = zis.getNextEntry()) != null) {
+ // if it is empty directory, create it
+ if (zEntry.isDirectory()) {
+ new File(destDir, zEntry.getName()).mkdirs();
+ continue;
+ }
+ // if it is a file, extract it
+ String filePath = zEntry.getName();
+ int lastSeparator = filePath.lastIndexOf("/");
+ String fileDir = "";
+ if (lastSeparator >= 0) {
+ fileDir = filePath.substring(0, lastSeparator);
+ }
+ //create directory for a file
+ new File(destDir, fileDir).mkdirs();
+ //write file
+ File outFile = new File(destDir, filePath);
+ fos = new FileOutputStream(outFile);
+ int n = 0;
+ while ((n = zis.read(buf)) >= 0) {
+ fos.write(buf, 0, n);
+ }
+ fos.close();
+ }
+ if (HelpBasePlugin.DEBUG_SEARCH) {
+ System.out.println(
+ "SearchIndex: Prebuilt index restored to " + destDir + ".");
+ }
+
+ } catch (IOException ioe) {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ioe2) {
+ }
+ }
+ } finally {
+ try {
+ zipIn.close();
+ if (zis != null)
+ zis.close();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchManager.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchManager.java
new file mode 100644
index 000000000..09fb82cdb
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchManager.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Manages indexing and search for all infosets
+ */
+public class SearchManager {
+ /** Search indexes, by locale */
+ private Map indexes = new HashMap();
+ /** Caches analyzer descriptors for each locale */
+ private Map analyzerDescriptors = new HashMap();
+ /** Progress Distributors indexed by index */
+ private Map progressDistibutors = new HashMap();
+
+ /**
+ * Constructs a Search manager.
+ */
+ public SearchManager() {
+ super();
+ }
+ public SearchIndex getIndex(String locale) {
+ synchronized (indexes) {
+ Object index = indexes.get(locale);
+ if (index == null) {
+ index = new SearchIndex(locale, getAnalyzer(locale));
+ indexes.put(locale, index);
+ }
+ return (SearchIndex) index;
+ }
+ }
+ private ProgressDistributor getProgressDistributor(SearchIndex index) {
+ synchronized (progressDistibutors) {
+ Object distributor = progressDistibutors.get(index);
+ if (distributor == null) {
+ distributor = new ProgressDistributor();
+ progressDistibutors.put(index, distributor);
+ }
+ return (ProgressDistributor) distributor;
+ }
+ }
+ /**
+ * Obtains AnalyzerDescriptor that indexing and search should
+ * use for a given locale.
+ * @param locale 2 or 5 character locale representation
+ */
+ private AnalyzerDescriptor getAnalyzer(String locale) {
+ // get an analyzer from cache
+ AnalyzerDescriptor analyzerDesc =
+ (AnalyzerDescriptor) analyzerDescriptors.get(locale);
+ if (analyzerDesc != null)
+ return analyzerDesc;
+
+ // obtain configured analyzer for this locale
+ analyzerDesc = new AnalyzerDescriptor(locale);
+ // save analyzer in the cache
+ analyzerDescriptors.put(locale, analyzerDesc);
+ String lang = analyzerDesc.getLang();
+ if (locale != null && !locale.equals(lang))
+ analyzerDescriptors.put(lang, analyzerDesc);
+
+ return analyzerDesc;
+ }
+
+ /**
+ * Searches index for documents containing an expression.
+ */
+ public void search(
+ ISearchQuery searchQuery,
+ ISearchHitCollector collector,
+ IProgressMonitor pm) {
+ SearchIndex index = getIndex(searchQuery.getLocale());
+ try {
+ updateIndex(pm, index);
+ if (!index.exists()) {
+ //no indexable documents, hence no index
+ //or index is corrupted
+ return;
+ }
+ } catch (IndexingOperation.IndexingException ie) {
+ if (HelpBasePlugin.DEBUG_SEARCH) {
+ System.out.println(
+ this.getClass().getName()
+ + " IndexUpdateException occured.");
+ }
+ }
+ index.search(searchQuery, collector);
+ }
+ /**
+ * Returns true when the index in the specified locale
+ * must be updated.
+ */
+ private boolean isIndexingNeeded(SearchIndex index) {
+ if (!index.exists()) {
+ return true;
+ }
+ return index.getDocPlugins().detectChange();
+ }
+ /**
+ * Updates index. Checks if all contributions were indexed.
+ * If not, it indexes them (Currently reindexes everything).
+ * @throws OperationCanceledException if indexing was cancelled
+ * @throws Exception if error occured
+ */
+ public void updateIndex(IProgressMonitor pm, SearchIndex index)
+ throws OperationCanceledException, IndexingOperation.IndexingException {
+ ProgressDistributor progressDistrib = getProgressDistributor(index);
+ progressDistrib.addMonitor(pm);
+ try {
+ synchronized (this) {
+ if (!isIndexingNeeded(index)) {
+ pm.beginTask("", 1);
+ pm.worked(1);
+ pm.done();
+ progressDistrib.removeMonitor(pm);
+ return;
+ }
+ if (HelpBasePlugin.DEBUG_SEARCH) {
+ System.out.println(
+ "SearchManager indexing " + index.getLocale());
+ }
+ // Perform indexing
+ try {
+ PluginVersionInfo versions = index.getDocPlugins();
+ if (versions == null) {
+ pm.beginTask("", 1);
+ pm.worked(1);
+ pm.done();
+ progressDistrib.removeMonitor(pm);
+ return;
+ }
+ IndexingOperation indexer = new IndexingOperation(index);
+ indexer.execute(progressDistrib);
+ } catch (OperationCanceledException oce) {
+ progressDistrib.operationCanceled();
+ HelpBasePlugin.logWarning(
+ Resources.getString("Search_cancelled"));
+ throw oce;
+ }
+ }
+ } finally {
+ progressDistrib.removeMonitor(pm);
+ }
+ }
+ /**
+ * Closes all indexes.
+ */
+ public void close() {
+ for (Iterator it = indexes.values().iterator(); it.hasNext();) {
+ ((SearchIndex) it.next()).close();
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchProgressMonitor.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchProgressMonitor.java
new file mode 100644
index 000000000..0fff840e8
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchProgressMonitor.java
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.util.*;
+
+import org.apache.lucene.search.*;
+import org.eclipse.core.boot.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+
+/**
+ * Progress monitor for search
+ * @since 2.0
+ */
+public class SearchProgressMonitor implements IProgressMonitor {
+
+ // Progress monitors, indexed by locale
+ private static Map progressMonitors = new HashMap();
+
+ // Dummy collector for triggering a progress monitor
+ private static ISearchHitCollector dummy_collector;
+
+ private boolean started, done, canceled;
+ private int totalWork = IProgressMonitor.UNKNOWN;
+ private int currWork;
+
+ static {
+ dummy_collector = new ISearchHitCollector() {
+ public void addHits(Hits h, String s) {
+ }
+ };
+ }
+
+ /**
+ * Constructor.
+ */
+ public SearchProgressMonitor() {
+ started = done = canceled = false;
+ }
+ public void beginTask(String name, int totalWork) {
+ this.totalWork = totalWork;
+ this.started = true;
+ }
+
+ public void done() {
+ currWork = totalWork;
+ this.done = true;
+ this.started = true;
+ }
+
+ public void setTaskName(String name) {
+ }
+
+ public void subTask(String name) {
+ }
+
+ public void worked(int work) {
+ currWork += work;
+ if (currWork > totalWork)
+ currWork = totalWork;
+ else if (currWork < 0)
+ currWork = 0;
+ }
+
+ public void internalWorked(double work) {
+ }
+
+ public int getPercentage() {
+ if (done) {
+ return 100;
+ }
+ if (totalWork == IProgressMonitor.UNKNOWN)
+ return 0;
+ if (currWork >= totalWork)
+ return 100;
+ return (int) (100 * currWork / totalWork);
+ }
+ /**
+ * Gets the isCancelled.
+ * @return Returns a boolean
+ */
+ public boolean isCanceled() {
+ return canceled;
+ }
+
+ /**
+ * Sets the isCanceled.
+ * @param isCancelled The isCanceled to set
+ */
+ public void setCancelled(boolean canceled) {
+ this.canceled = canceled;
+ }
+
+ /**
+ * Gets the isStarted.
+ * @return Returns a boolean
+ */
+ public boolean isStarted() {
+ return started;
+ }
+
+ /**
+ * Gets the isDone.
+ * @return Returns a boolean
+ */
+ public boolean isDone() {
+ return done;
+ }
+
+ /**
+ * Sets the isCanceled.
+ * @param isCanceled The isCanceled to set
+ */
+ public void setCanceled(boolean canceled) {
+ this.canceled = canceled;
+ }
+
+ /**
+ * Returns a progress monitor for specified query and locale
+ */
+ public static synchronized SearchProgressMonitor getProgressMonitor(final String locale) {
+
+ // return an existing progress monitor if there is one
+ if (progressMonitors.get(locale) != null)
+ return (SearchProgressMonitor) progressMonitors.get(locale);
+
+ final SearchProgressMonitor pm = new SearchProgressMonitor();
+ progressMonitors.put(locale, pm);
+
+ // spawn a thread that will cause indexing if needed
+ Thread indexer = new Thread(new Runnable() {
+ public void run() {
+ try {
+ HelpSystem.getSearchManager().search(
+ new DummySearchQuery(locale),
+ dummy_collector,
+ pm);
+ } catch (OperationCanceledException oce) {
+ // operation cancelled
+ // throw out the progress monitor
+ progressMonitors.remove(locale);
+ } catch (Exception e) {
+ progressMonitors.remove(locale);
+ if (BootLoader.isRunning()) {
+ HelpBasePlugin.logError(
+ Resources.getString("search_index_update_error"),
+ e);
+ }else{
+ // Platform has shut down
+ }
+ }
+ }
+ });
+ indexer.setName("HelpSearchIndexer");
+ indexer.start();
+ // give pm chance to start
+ // this will avoid seing progress if there is no work to do
+ while (!pm.isStarted()) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException ie) {
+ }
+ if (progressMonitors.get(locale) == null)
+ // operation got canceled
+ break;
+ }
+
+ return pm;
+ }
+ static class DummySearchQuery implements ISearchQuery {
+ private String l;
+ DummySearchQuery(String loc) {
+ l = loc;
+ }
+ /**
+ * Obtains names of fields in addition to default field
+ */
+ public Collection getFieldNames() {
+ return new ArrayList();
+ }
+ /**
+ * Obtains search word (user query)
+ */
+ public String getSearchWord() {
+ return "dummy";
+ }
+ /**
+ * @return true if search only in specified fields, not the default field
+ */
+ public boolean isFieldSearch() {
+ return false;
+ }
+ /**
+ * Obtains locale
+ */
+ public String getLocale() {
+ return l;
+ }
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchQuery.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchQuery.java
new file mode 100644
index 000000000..d651d41c0
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchQuery.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.util.*;
+
+import org.eclipse.core.boot.*;
+
+/**
+ * An implementation of ISearchQuery.
+ */
+public class SearchQuery implements ISearchQuery {
+ Collection fieldNames;
+ boolean fieldSearch;
+ String locale;
+ String searchWord;
+ public SearchQuery() {
+ this("", false, new ArrayList(), BootLoader.getNL());
+ }
+ public SearchQuery(
+ String searchWord,
+ boolean fieldSearch,
+ Collection fieldNames,
+ String locale) {
+ this.searchWord = searchWord;
+ this.fieldSearch = fieldSearch;
+ this.fieldNames = fieldNames;
+ this.locale = locale;
+ }
+ /**
+ * Returns the fieldNames.
+ * @return Collection
+ */
+ public Collection getFieldNames() {
+ return fieldNames;
+ }
+
+ /**
+ * Returns the fieldSearch.
+ * @return boolean
+ */
+ public boolean isFieldSearch() {
+ return fieldSearch;
+ }
+
+ /**
+ * Returns the locale.
+ * @return String
+ */
+ public String getLocale() {
+ return locale;
+ }
+
+ /**
+ * Returns the searchWord.
+ * @return String
+ */
+ public String getSearchWord() {
+ return searchWord;
+ }
+
+ /**
+ * Sets the fieldNames.
+ * @param fieldNames The fieldNames to set
+ */
+ public void setFieldNames(Collection fieldNames) {
+ this.fieldNames = fieldNames;
+ }
+
+ /**
+ * Sets the fieldSearch.
+ * @param fieldSearch The fieldSearch to set
+ */
+ public void setFieldSearch(boolean fieldSearch) {
+ this.fieldSearch = fieldSearch;
+ }
+
+ /**
+ * Sets the locale.
+ * @param locale The locale to set
+ */
+ public void setLocale(String locale) {
+ this.locale = locale;
+ }
+
+ /**
+ * Sets the searchWord.
+ * @param searchWord The searchWord to set
+ */
+ public void setSearchWord(String searchWord) {
+ this.searchWord = searchWord;
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchResults.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchResults.java
new file mode 100644
index 000000000..ea539720e
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchResults.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.lucene.search.*;
+import org.eclipse.help.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+import org.eclipse.help.internal.workingset.*;
+
+/**
+ * Search result collector.
+ * Performs filtering and collects hits into an array of SearchHit
+ */
+public class SearchResults implements ISearchHitCollector {
+ // Collection of WorkingSet
+ private ArrayList scopes;
+ private int maxHits;
+ private String locale;
+ protected SearchHit[] searchHits = new SearchHit[0];
+ /**
+ * Constructor
+ * @param workingSets working sets or null if no filtering
+ */
+ public SearchResults(WorkingSet[] workingSets, int maxHits, String locale) {
+ this.maxHits = maxHits;
+ this.locale = locale;
+ this.scopes = getScopes(workingSets);
+ }
+ /**
+ * Adds hits to the result
+ * @param Hits hits
+ */
+ public void addHits(Hits hits, String highlightTerms) {
+ String urlEncodedWords = URLCoder.encode(highlightTerms);
+ List searchHitList = new ArrayList();
+ float scoreScale = 1.0f;
+ boolean scoreScaleSet = false;
+ // need to keep track of previous score to work around
+ // workaround for bug in Lucene 1.2.0 final
+ float lastScore = Float.MAX_VALUE;
+ for (int h = 0; h < hits.length() && h < maxHits; h++) {
+ org.apache.lucene.document.Document doc;
+ float score;
+ try {
+ doc = hits.doc(h);
+ score = hits.score(h);
+ } catch (IOException ioe) {
+ continue;
+ }
+ String href = doc.get("name");
+
+ IToc toc = null; // the TOC containing the topic
+ AdaptableHelpResource scope = null;
+ // the scope for the topic, if any
+ if (scopes == null) {
+ toc = getTocForTopic(href, locale);
+ } else {
+ scope = getScopeForTopic(href);
+ if (scope == null)
+ continue;
+ else if (scope instanceof AdaptableToc)
+ toc = (IToc) scope.getAdapter(IToc.class);
+ else // scope is AdaptableTopic
+ toc = (IToc) scope.getParent().getAdapter(IToc.class);
+ }
+
+ // adjust score
+ if (!scoreScaleSet) {
+ if (score > 0) {
+ lastScore = score;
+ scoreScale = 0.99f / score;
+ score = 1;
+ }
+ scoreScaleSet = true;
+ } else {
+ // workaround for bug in Lucene 1.2.0 final
+ // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=12273
+ if (score > lastScore) {
+ scoreScale = scoreScale * lastScore / score;
+ }
+ lastScore = score;
+ //
+ score = score * scoreScale + 0.01f;
+ }
+
+ // Set the document label
+ String label = doc.get("raw_title");
+ if ("".equals(label) && toc != null) {
+ if (scope != null) {
+ label = scope.getTopic(href).getLabel();
+ } else
+ label = toc.getTopic(href).getLabel();
+ }
+ if (label == null || "".equals(label))
+ label = href;
+
+ // Set document href
+ href = href + "?resultof=" + urlEncodedWords;
+ searchHitList.add(new SearchHit(href, label, score, toc));
+ }
+ searchHits =
+ (SearchHit[]) searchHitList.toArray(
+ new SearchHit[searchHitList.size()]);
+
+ }
+ /**
+ * Finds a topic within a scope
+ */
+ private AdaptableHelpResource getScopeForTopic(String href) {
+ for (int i = 0; i < scopes.size(); i++) {
+ AdaptableHelpResource scope = (AdaptableHelpResource) scopes.get(i);
+ if (scope.getTopic(href) != null)
+ return scope;
+ }
+ return null;
+ }
+
+ /**
+ * Finds a topic in a toc
+ * or within a scope if specified
+ */
+ private IToc getTocForTopic(String href, String locale) {
+ IToc[] tocs = HelpCore.getTocManager().getTocs(locale);
+ for (int i = 0; i < tocs.length; i++) {
+ ITopic topic = tocs[i].getTopic(href);
+ if (topic != null)
+ return tocs[i];
+ }
+ return null;
+ }
+
+ /**
+ * Gets the searchHits.
+ * @return Returns a SearchHit[]
+ */
+ public SearchHit[] getSearchHits() {
+ return searchHits;
+ }
+
+ /**
+ * Returns a collection of adaptable help resources that are roots for
+ * filtering.
+ * @return Collection
+ */
+ private ArrayList getScopes(WorkingSet[] wSets) {
+ if (wSets == null)
+ return null;
+
+ scopes = new ArrayList(wSets.length);
+ for (int w=0; w<wSets.length;w++) {
+ AdaptableHelpResource[] elements = wSets[w].getElements();
+ for (int i = 0; i < elements.length; i++)
+ scopes.add(elements[i]);
+ }
+ return scopes;
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/SmartAnalyzer.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SmartAnalyzer.java
new file mode 100644
index 000000000..be8b555b7
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/SmartAnalyzer.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.io.Reader;
+
+import org.apache.lucene.analysis.*;
+
+/**
+ * Smart Analyzer. Chooses underlying implementation
+ * based on the field which text is analyzed.
+ */
+public class SmartAnalyzer extends Analyzer {
+ Analyzer pluggedInAnalyzer;
+ Analyzer exactAnalyzer;
+
+ /**
+ * Constructor for SmartAnalyzer.
+ */
+ public SmartAnalyzer(String locale, Analyzer pluggedInAnalyzer) {
+ super();
+ this.pluggedInAnalyzer = pluggedInAnalyzer;
+ this.exactAnalyzer = new DefaultAnalyzer(locale);
+ }
+ /**
+ * Creates a TokenStream which tokenizes all the text
+ * in the provided Reader.
+ * Delegates to DefaultAnalyzer when field used to search for exact match,
+ * and to plugged-in analyzer for other fields.
+ */
+ public final TokenStream tokenStream(String fieldName, Reader reader) {
+ if (fieldName != null && fieldName.startsWith("exact_")) {
+ return exactAnalyzer.tokenStream(fieldName, reader);
+ }
+ return pluggedInAnalyzer.tokenStream(fieldName, reader);
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/search/WordTokenStream.java b/org.eclipse.help.base/src/org/eclipse/help/internal/search/WordTokenStream.java
new file mode 100644
index 000000000..0c8a3ce99
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/search/WordTokenStream.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.search;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+import org.apache.lucene.analysis.*;
+
+/**
+ * WordTokenStream obtains tokens containing words
+ * appropriate for use with Lucene search engine.
+ */
+public final class WordTokenStream extends TokenStream {
+ private static final int BUF_LEN = 4096;
+ private static final int TOKENS_LEN = 512;
+ private final String fieldName;
+ private final Reader reader;
+ private final BreakIterator boundary;
+ private final ArrayList tokens;
+ private int token;
+ private int noTokens;
+ private final char[] cbuf;
+ /**
+ * Constructor
+ */
+ public WordTokenStream(String fieldName, Reader reader, Locale locale) {
+ this.fieldName = fieldName;
+ this.reader = reader;
+ boundary = BreakIterator.getWordInstance(locale);
+ cbuf = new char[BUF_LEN];
+ tokens = new ArrayList(TOKENS_LEN);
+
+ }
+ /**
+ * @see TokenStream#next()
+ */
+ public final Token next() throws IOException {
+ if (token >= noTokens) {
+ // read BUF_LEN of chars
+ int l;
+ while ((l = reader.read(cbuf)) <= 0) {
+ if (l < 0) {
+ // EOF
+ reader.close();
+ return null;
+ }
+ }
+ StringBuffer strbuf = new StringBuffer(l + 80);
+ strbuf.append(cbuf, 0, l);
+ // read more until white space (or EOF)
+ int c;
+ while (0 <= (c = reader.read())) {
+ strbuf.append((char) c);
+ if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
+ break;
+ }
+ }
+
+ String str = strbuf.toString();
+ boundary.setText(str);
+
+ int start = boundary.first();
+ tokens.clear();
+ wordsbreak : for (
+ int end = boundary.next();
+ end != BreakIterator.DONE;
+ start = end, end = boundary.next()) {
+ // 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.isLetterOrDigit(str.charAt(i))) {
+ // it is a word
+ tokens.add(
+ new Token(str.substring(start, end), start, end));
+ continue wordsbreak;
+ }
+ }
+ }
+
+ if (c < 0) {
+ reader.close();
+ tokens.add((Token) null);
+ }
+ noTokens = tokens.size();
+ token = 0;
+ }
+
+ return (Token) tokens.get(token++);
+
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Eclipse.java b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Eclipse.java
new file mode 100644
index 000000000..474294fa9
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Eclipse.java
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.standalone;
+
+import java.io.*;
+import java.util.List;
+
+/**
+ * Eclipse launcher. Spawns eclipse executable
+ * or org.eclipse.core.launcher.Main.
+ */
+public class Eclipse extends Thread {
+ // Eclipse exit codes
+ private static final int NEEDS_RESTART = 23;
+ // Launching status
+ public static final int STATUS_INIT = 0;
+ public static final int STATUS_STARTED = 1;
+ public static final int STATUS_ERROR = 2;
+
+ File dir;
+ String[] cmdarray;
+ private int status;
+ private Exception exception = new Exception("Unknown exception.");
+ Process pr;
+ /**
+ * Constructor
+ */
+ public Eclipse() {
+ super();
+ this.setName("Eclipse");
+ this.dir = Options.getEclipseHome();
+ }
+ private void prepareCommand() throws Exception {
+ if (Options.useExe()) {
+ prepareEclipseCommand();
+ ensureEclipseExeExists();
+ } else {
+ prepareJavaCommand();
+ ensureStartupJarExists();
+ }
+ ensureVmExists();
+ }
+ private void prepareEclipseCommand() {
+ List vmArgs = Options.getVmArgs();
+ List eclipseArgs = Options.getEclipseArgs();
+ cmdarray = new String[3 + vmArgs.size() + 1 + eclipseArgs.size()];
+ cmdarray[0] =
+ new File(Options.getEclipseHome(), "eclipse").getAbsolutePath();
+ cmdarray[1] = "-vm";
+ cmdarray[2] = Options.getVm();
+ for (int i = 0; i < eclipseArgs.size(); i++) {
+ cmdarray[3 + i] = (String) eclipseArgs.get(i);
+ }
+ cmdarray[3 + eclipseArgs.size()] = "-vmargs";
+ for (int i = 0; i < vmArgs.size(); i++) {
+ cmdarray[4 + eclipseArgs.size() + i] = (String) vmArgs.get(i);
+ }
+ }
+ private void prepareJavaCommand() {
+ List vmArgs = Options.getVmArgs();
+ List eclipseArgs = Options.getEclipseArgs();
+ cmdarray = new String[1 + vmArgs.size() + 3 + eclipseArgs.size()];
+ cmdarray[0] = Options.getVm();
+ for (int i = 0; i < vmArgs.size(); i++) {
+ cmdarray[1 + i] = (String) vmArgs.get(i);
+ }
+ cmdarray[1 + vmArgs.size()] = "-cp";
+ cmdarray[2 + vmArgs.size()] = "startup.jar";
+ cmdarray[3 + vmArgs.size()] = "org.eclipse.core.launcher.Main";
+ for (int i = 0; i < eclipseArgs.size(); i++) {
+ cmdarray[4 + vmArgs.size() + i] = (String) eclipseArgs.get(i);
+ }
+ }
+ /**
+ * Launches Eclipse process and waits for it.
+ */
+ public void run() {
+ try {
+ prepareCommand();
+ if (Options.isDebug()) {
+ printCommand();
+ }
+ launchProcess();
+ } catch (Exception exc) {
+ exception = exc;
+ status = STATUS_ERROR;
+ } finally {
+ if (status == STATUS_INIT) {
+ status = STATUS_ERROR;
+ }
+ }
+ }
+ private void launchProcess() throws IOException {
+ try {
+ do {
+ pr = Runtime.getRuntime().exec(cmdarray, (String[]) null, dir);
+ (new StreamConsumer(pr.getInputStream())).start();
+ (new StreamConsumer(pr.getErrorStream())).start();
+ status = STATUS_STARTED;
+ pr.waitFor();
+ if (Options.isDebug()) {
+ System.out.println(
+ "Eclipse exited with status code " + pr.exitValue());
+ if (pr.exitValue() == NEEDS_RESTART) {
+ System.out.println(
+ "Updates are installed, Eclipse will be restarted.");
+ }
+ }
+ } while (pr.exitValue() == NEEDS_RESTART);
+ } catch (InterruptedException e) {
+ }
+ }
+ /**
+ * Reads a stream
+ */
+ public class StreamConsumer extends Thread {
+ BufferedReader bReader;
+ public StreamConsumer(InputStream inputStream) {
+ super();
+ this.setName("Eclipse out/err consumer");
+ this.setDaemon(true);
+ bReader = new BufferedReader(new InputStreamReader(inputStream));
+ }
+ public void run() {
+ try {
+ String line;
+ while (null != (line = bReader.readLine())) {
+ System.out.println(line);
+ }
+ bReader.close();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+ private void ensureVmExists() throws Exception {
+ File vmExe = new File(Options.getVm());
+ if (vmExe.exists() && !vmExe.isDirectory()) {
+ return;
+ }
+ vmExe = new File(Options.getVm() + ".exe");
+ if (vmExe.exists() && !vmExe.isDirectory()) {
+ return;
+ }
+ throw new Exception(
+ "File "
+ + vmExe.getAbsolutePath()
+ + " does not exists. Pass a correct -vm option");
+ }
+ private void ensureEclipseExeExists() throws Exception {
+ File eclipseExe =
+ new File(
+ Options.getEclipseHome(),
+ "eclipse"
+ + (System.getProperty("os.name").startsWith("Win")
+ ? ".exe"
+ : ""));
+ if (eclipseExe.exists() && !eclipseExe.isDirectory()) {
+ return;
+ }
+ throw new Exception(
+ "File "
+ + eclipseExe.getAbsolutePath()
+ + " does not exists. Pass a correct -eclipsehome option");
+ }
+ private void ensureStartupJarExists() throws Exception {
+ File startupJar = new File(Options.getEclipseHome(), "startup.jar");
+ if (startupJar.exists() && !startupJar.isDirectory()) {
+ return;
+ }
+ throw new Exception(
+ "File "
+ + startupJar.getAbsolutePath()
+ + " does not exists. Pass a correct -eclipsehome option");
+ }
+ /**
+ * @return Exception
+ */
+ public Exception getException() {
+ return exception;
+ }
+
+ /**
+ * @return int
+ */
+ public int getStatus() {
+ return status;
+ }
+ private void printCommand() {
+ System.out.println("Launch command is:");
+ for (int i = 0; i < cmdarray.length; i++) {
+ System.out.println(" " + (String) cmdarray[i]);
+ }
+
+ }
+ /**
+ * Used in unit testing.
+ */
+ public void killProcess() {
+ if (pr != null) {
+ pr.destroy();
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseConnection.java b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseConnection.java
new file mode 100644
index 000000000..59bf71bda
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseConnection.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.standalone;
+
+import java.io.*;
+import java.net.*;
+import java.util.Properties;
+
+/**
+ * This program is used to start or stop Eclipse
+ * Infocenter application.
+ * It should be launched from command line.
+ */
+public class EclipseConnection {
+ // timout for .hostport file to apper since starting eclipse [ms]
+ // 0 if no waiting for file should occur
+ int startupTimeout;
+ // number of retries to connectect to webapp
+ int connectionRetries;
+ // time between retries to connectect to webapp [ms]
+ int connectionRetryInterval;
+ // help server host
+ private String host;
+ // help server port
+ private String port;
+
+ public EclipseConnection() {
+ this(0, 0, 5 * 1000);
+ }
+
+ public EclipseConnection(
+ int startupTimeout,
+ int connectionRetries,
+ int connectionRetryInterval) {
+
+ this.startupTimeout = startupTimeout;
+ this.connectionRetries = connectionRetries;
+ this.connectionRetryInterval = connectionRetryInterval;
+ }
+
+ public String getPort() {
+ return port;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void reset() {
+ host = null;
+ port = null;
+ }
+
+ public boolean isValid() {
+ return (host != null && port != null);
+ }
+
+ public void connect(URL url) throws InterruptedException, Exception {
+ for (int i = 0; i <= connectionRetries; i++) {
+ try {
+ HttpURLConnection connection =
+ (HttpURLConnection) url.openConnection();
+ if (Options.isDebug()) {
+ System.out.println(
+ "Connection to control servlet created.");
+ }
+ connection.connect();
+ if (Options.isDebug()) {
+ System.out.println(
+ "Connection to control servlet connected.");
+ }
+ int code = connection.getResponseCode();
+ if (Options.isDebug()) {
+ System.out.println(
+ "Response code from control servlet=" + code);
+ }
+ connection.disconnect();
+ return;
+ } catch (IOException ioe) {
+ if (Options.isDebug()) {
+ ioe.printStackTrace();
+ }
+ }
+ Thread.sleep(connectionRetryInterval);
+ }
+ throw new Exception("Connection to Help System timed out.");
+ }
+
+ /**
+ * Obtains host and port from the file.
+ * Retries several times if file does not exists,
+ * and help might be starting up.
+ */
+ public void renew() throws Exception {
+ long time1 = System.currentTimeMillis();
+ while (!Options.getConnectionFile().exists()) {
+ // wait for .hostport file to appear
+ if (Options.isDebug()) {
+ System.out.println(
+ "File "
+ + Options.getConnectionFile()
+ + " does not exist, at the moment.");
+ }
+ // timeout
+ if (System.currentTimeMillis() - time1 >= startupTimeout) {
+ if (Options.isDebug()) {
+ System.out.println(
+ "Timeout waiting for file "
+ + Options.getConnectionFile()+"\nEclipse is not running.");
+ }
+ throw new Exception(
+ "Timeout waiting for file " + Options.getConnectionFile()+"\nEclipse is not running.");
+ }
+ // wait more
+ Thread.sleep(2000);
+ }
+ Properties p = new Properties();
+ FileInputStream is = null;
+ try {
+ is = new FileInputStream(Options.getConnectionFile());
+ p.load(is);
+ is.close();
+ } catch (IOException ioe) {
+ // it ok, eclipse might have just exited
+ throw ioe;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ioe2) {
+ }
+ }
+ }
+ host = (String) p.get("host");
+ port = (String) p.get("port");
+ if (Options.isDebug()) {
+ System.out.println("Help server host=" + host);
+ }
+ if (Options.isDebug()) {
+ System.out.println("Help server port=" + port);
+ }
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseController.java b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseController.java
new file mode 100644
index 000000000..fa96b67e2
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/EclipseController.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.standalone;
+
+import java.net.*;
+
+/**
+ * This program is used to start or stop Eclipse
+ * Infocenter application.
+ * It should be launched from command line.
+ */
+public class EclipseController {
+
+ // control servlet path
+ private static final String CONTROL_SERVLET_PATH =
+ "/helpControl/control.html";
+
+ // application to launch
+ protected String applicationId;
+
+ // Eclipse connection params
+ protected EclipseConnection connection;
+
+ private Eclipse eclipse = null;
+ /**
+ * Constructs help system
+ * @param applicationID ID of Eclipse help application
+ * @param args array of String options and their values
+ * Option <code>-eclipseHome dir</code> specifies Eclipse
+ * installation directory.
+ * It must be provided, when current directory is not the same
+ * as Eclipse installation directory.
+ * Additionally, most options accepted by Eclipse execuable are supported.
+ */
+ public EclipseController(String applicationId, String[] args) {
+
+ this.applicationId = applicationId;
+ Options.init(applicationId, args);
+ connection = initConnection();
+ }
+
+ /**
+ * Creates a connection to Eclipse. May need to override this to pass retry parameters.
+ */
+ protected EclipseConnection initConnection() {
+ return new EclipseConnection();
+ }
+
+ /**
+ * @see org.eclipse.help.standalone.Help#shutdown()
+ */
+ public void shutdown() throws Exception {
+ try {
+ sendHelpCommand("shutdown", new String[0]);
+ } catch (MalformedURLException mue) {
+ mue.printStackTrace();
+ } catch (InterruptedException ie) {
+ }
+
+ connection.reset();
+ }
+
+ /**
+ * @see org.eclipse.help.standalone.Help#start()
+ */
+ public void start() throws Exception {
+ connection.reset();
+ startEclipse();
+ }
+
+ /**
+ * Ensures the application is running, and sends command
+ * to the control servlet.
+ * If connection fails, retries several times,
+ * in case webapp is starting up.
+ */
+ protected void sendHelpCommand(String command, String[] parameters)
+ throws Exception {
+ if (!"shutdown".equalsIgnoreCase(command)) {
+ if (eclipse == null || !eclipse.isAlive()) {
+ startEclipse();
+ }
+ }
+ if (!connection.isValid()) {
+ connection.renew();
+ }
+
+ try {
+ URL url = createCommandURL(command, parameters);
+ connection.connect(url);
+ } catch (MalformedURLException mue) {
+ mue.printStackTrace();
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ /**
+ * Builds a URL that communicates the specified command
+ * to help control servlet.
+ * @param command standalone help system command e.g. "displayHelp"
+ * @param parameters array of parameters of the command e.g. {"http://www.eclipse.org"}
+ */
+ private URL createCommandURL(String command, String[] parameters)
+ throws MalformedURLException {
+ StringBuffer urlStr = new StringBuffer();
+ urlStr.append("http://");
+ urlStr.append(connection.getHost());
+ urlStr.append(":");
+ urlStr.append(connection.getPort());
+ urlStr.append(CONTROL_SERVLET_PATH);
+ urlStr.append("?command=");
+ urlStr.append(command);
+ for (int i = 0; i < parameters.length; i++) {
+ urlStr.append("&");
+ urlStr.append(parameters[i]);
+ }
+ if (Options.isDebug()) {
+ System.out.println("Control servlet URL=" + urlStr.toString());
+ }
+ return new URL(urlStr.toString());
+ }
+
+ /**
+ * Starts Eclipse if not yet running.
+ */
+ private void startEclipse() throws Exception {
+ if (Options.isDebug()) {
+ System.out.println(
+ "Using workspace " + Options.getWorkspace().getAbsolutePath());
+ System.out.println(
+ "Checking if file " + Options.getLockFile() + " exists.");
+ }
+ if (isAnotherRunning()) {
+ return;
+ }
+ // delete old connection file
+ Options.getConnectionFile().delete();
+
+ if (Options.isDebug()) {
+ System.out.println(
+ "Ensured old .connection file is deleted. Launching Eclipse.");
+ }
+ eclipse = new Eclipse();
+ eclipse.start();
+ while (eclipse.getStatus() == Eclipse.STATUS_INIT) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException ie) {
+ }
+ }
+ if (eclipse.getStatus() == Eclipse.STATUS_ERROR) {
+ throw eclipse.getException();
+ }
+ if (Options.isDebug()) {
+ System.out.println("Eclipse launched");
+ }
+ }
+
+ /**
+ * @return true if eclipse is already running in another process
+ */
+ private boolean isAnotherRunning() {
+ if (!Options.getLockFile().exists()) {
+ if (Options.isDebug()) {
+ System.out.println(
+ "File "
+ + Options.getLockFile()
+ + " does not exist. Eclipse needs to be started.");
+ }
+ return false;
+ }
+
+ if (System.getProperty("os.name").startsWith("Win")) {
+ // if file cannot be deleted, Eclipse is running
+ if (!Options.getLockFile().delete()) {
+ if (Options.isDebug()) {
+ System.out.println(
+ "File "
+ + Options.getLockFile()
+ + " is locked. Eclipse is already running.");
+ }
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ // if connection to control servlet can be made, Eclipse is running
+ try {
+ connection.renew();
+ if (connection.getHost() != null
+ && connection.getPort() != null) {
+ URL url = createCommandURL("test", new String[0]);
+ connection.connect(url);
+ if (Options.isDebug()) {
+ System.out.println(
+ "Test connection to Eclipse established. No need to start new Eclipse instance.");
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ }
+ if (Options.isDebug()) {
+ System.out.println(
+ "Test connection to Eclipse could not be established. Eclipse instance needs to be started.");
+ }
+ connection.reset();
+ return false;
+ }
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Options.java b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Options.java
new file mode 100644
index 000000000..372e738fc
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/Options.java
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.standalone;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Options for starting stand alone help and infocenter.
+ */
+public class Options {
+ // debugging
+ private static boolean debug = false;
+ // use eclipse.exe
+ private static boolean useExe = true;
+ // Eclipse installation directory
+ private static File eclipseHome;
+ // workspace directory to be used by Eclipse
+ private static File workspace;
+ // Eclipse .lock file
+ private static File lockFile;
+ // .hostport file to obtain help server host and port from Eclipse help application
+ private static File hostPortFile;
+ // vm to use
+ private static String vm;
+ // arguments to pass to Eclipse
+ private static List vmArgs;
+ // arguments to pass to VM
+ private static List eclipseArgs;
+ // help command to execute
+ private static List helpCommand;
+ // host to override appserver preferences
+ private static String host;
+ // port to override appserver preferences
+ private static String port;
+ // time to wait for help system to start,
+ // before sending command is possible
+ private static int serverTimeout;
+ /**
+ * Initializes options.
+ * @param appId eclipse application id
+ * @param args array of String options and their values
+ * Option <code>-eclipseHome dir</code> specifies Eclipse
+ * installation directory.
+ * It must be provided, when current directory is not the same
+ * as Eclipse installation directory.
+ * Additionally, most options accepted by Eclipse execuable are supported.
+ */
+ public static void init(String appId, String[] args) {
+ // convert array of arguments to a list
+ List list = new ArrayList();
+ for (int i = 0; i < args.length; i++) {
+ list.add(args[i]);
+ }
+
+ init(appId, list);
+ }
+ /**
+ * Initializes options.
+ * @param appId eclipse application id
+ * @param options list of options and their values
+ * Option <code>-eclipseHome dir</code> specifies Eclipse
+ * installation directory.
+ * It must be provided, when current directory is not the same
+ * as Eclipse installation directory.
+ * Additionally, most options accepted by Eclipse execuable are supported.
+ */
+ public static void init(String appId, List options) {
+ // Initialize eclipseArgs with all passed options
+ eclipseArgs = new ArrayList();
+ eclipseArgs.addAll(options);
+
+ // consume -command option
+ helpCommand = extractOption(eclipseArgs, "-command");
+ if(helpCommand==null){
+ helpCommand=new ArrayList(0);
+ }
+
+ // read -debug option
+ if (getOption(eclipseArgs, "-debug") != null) {
+ debug = true;
+ System.out.println("Debugging is on.");
+ }
+ // consume -noexec option
+ if (extractOption(eclipseArgs, "-noexec") != null) {
+ useExe = false;
+ }
+ // consume -eclipsehome (accept eclipse_home too) option
+ List homes = extractOption(eclipseArgs, "-eclipseHome");
+ if (homes==null || homes.isEmpty()) {
+ homes = extractOption(eclipseArgs, "-eclipse_Home");
+ }
+ if (homes!=null && !homes.isEmpty()) {
+ eclipseHome = new File((String) homes.get(0));
+ } else {
+ eclipseHome = new File(System.getProperty("user.dir"));
+ }
+
+ // read -data option
+ List workspaces = getOption(eclipseArgs, "-data");
+ if (workspaces != null && !workspaces.isEmpty()) {
+ workspace = new File((String) workspaces.get(0));
+ } else {
+ workspace = new File(eclipseHome, "workspace");
+ }
+ lockFile = new File(workspace, "/.metadata/.lock");
+ hostPortFile = new File(workspace, "/.metadata/.connection");
+
+ // consume -host option
+ List hosts = extractOption(eclipseArgs, "-host");
+ if (hosts != null && hosts.size() > 0) {
+ host = (String) hosts.get(0);
+ }
+
+ // consume -port option
+ List ports = extractOption(eclipseArgs, "-port");
+ if (ports != null && ports.size() > 0) {
+ port = (String) ports.get(0);
+ }
+
+ // consume -servertimout option
+ serverTimeout = 0;
+ List timeouts = extractOption(eclipseArgs, "-servertimeout");
+ if (timeouts != null && timeouts.size() > 0) {
+ try {
+ int timeout = Integer.parseInt((String) timeouts.get(0));
+ if (timeout >= 0) {
+ serverTimeout = timeout;
+ }
+ } catch (NumberFormatException nfe) {
+ }
+ }
+
+ // consume -vm option
+ List vms = extractOption(eclipseArgs, "-vm");
+ if (vms != null && !vms.isEmpty()) {
+ vm = (String) vms.get(0);
+ } else {
+ String vmName = System.getProperty("java.vm.name");
+ String executable = "J9".equals(vmName) ? "j9" : "java";
+ if (System.getProperty("os.name").startsWith("Win")) {
+ executable += "w.exe";
+ }
+ vm =
+ System.getProperty("java.home")
+ + File.separator
+ + "bin"
+ + File.separator
+ + executable;
+ }
+
+ // consume -vmargs option
+ vmArgs=new ArrayList(0);
+ List passedVmArgs = extractOption(eclipseArgs, "-vmargs");
+ if (passedVmArgs!=null && passedVmArgs.size() > 0) {
+ vmArgs=passedVmArgs;
+ }
+
+ // modify the options for passing them to eclipse
+ // add -application option
+ extractOption(eclipseArgs, "-application");
+ eclipseArgs.add(0, "-application");
+ eclipseArgs.add(1, appId);
+
+ // add -nosplash option (prevent splash)
+ extractOption(eclipseArgs, "-showsplash");
+ extractOption(eclipseArgs, "-endsplash");
+ extractOption(eclipseArgs, "-nosplash");
+ eclipseArgs.add(0, "-nosplash");
+
+ // add server_host and/or port to -vmargs option
+ if (host != null || port != null) {
+ if (host != null) {
+ vmArgs.add("-Dserver_host=" + host);
+ }
+ if (port != null) {
+ vmArgs.add("-Dserver_port=" + port);
+ }
+ }
+ }
+
+ /**
+ * Returns true if debugging is enabled
+ */
+ public static boolean isDebug() {
+ return debug;
+ }
+
+ public static File getConnectionFile() {
+ return hostPortFile;
+ }
+
+ public static File getLockFile() {
+ return lockFile;
+ }
+
+ public static File getEclipseHome() {
+ return eclipseHome;
+ }
+
+ public static File getWorkspace() {
+ return workspace;
+ }
+
+ public static List getHelpCommand() {
+ return helpCommand;
+ }
+
+ public static List getEclipseArgs() {
+ return eclipseArgs;
+ }
+
+ /**
+ * Returns the serverTimeout.
+ * @return number of seconds to wait for the server,
+ * 0 when not set, and default should be used
+ */
+ public static int getServerTimeout() {
+ return serverTimeout;
+ }
+
+ /**
+ * Removes specified option and its list of values
+ * from a list of options
+ * @param optionName name of the option e.g. -data
+ * @return List of String values of the specified option,
+ * or null if option is not present
+ */
+ private static List extractOption(List options, String optionName) {
+ List values = null;
+ for (int i = 0; i < options.size();) {
+ if (optionName.equalsIgnoreCase((String) options.get(i))) {
+ if (values == null) {
+ values = new ArrayList(1);
+ }
+ // found the option, remove option
+ options.remove(i);
+ // remove option parameters
+ while (i < options.size()) {
+ if (((String) options.get(i)).startsWith("-")
+ && !optionName.equals("-vmargs")) {
+ // start of next option
+ break;
+ }
+ // note, and remove option value
+ values.add(options.get(i));
+ options.remove(i);
+ }
+ } else {
+ i++;
+ }
+ }
+ return values;
+ }
+
+ /**
+ * Obtains specified option and its list of values
+ * from a list of options
+ * @param optionName name of the option e.g. -data
+ * @param options List of Eclipse options
+ * @return List of String values of the specified option,
+ * or null if option is not present
+ */
+ private static List getOption(List options, String optionName) {
+ List values = null;
+ for (int i = 0; i < options.size(); i++) {
+ if (optionName.equalsIgnoreCase((String) options.get(i))) {
+ if (values == null) {
+ values = new ArrayList(1);
+ }
+ // read option parameters
+ for (int j = i + 1; j < options.size(); j++) {
+ if (((String) options.get(j)).startsWith("-")
+ && !optionName.equals("-vmargs")) {
+ // start of next option
+ i = j;
+ break;
+ }
+ values.add(options.get(j));
+ }
+ }
+ }
+ return values;
+ }
+ public static String getVm() {
+ return vm;
+ }
+ public static List getVmArgs() {
+ return vmArgs;
+ }
+ /**
+ * Returns the useExe.
+ * @return boolean
+ */
+ public static boolean useExe() {
+ return useExe;
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneHelp.java b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneHelp.java
new file mode 100644
index 000000000..83386d6d8
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneHelp.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.standalone;
+
+import java.util.List;
+
+import org.eclipse.help.internal.HelpBasePlugin;
+
+/**
+ * This is a standalone help system. It takes care of
+ * launching the eclipse with its help system implementation,
+ * and controling it.
+ * This class can be used instantiated and used in a Java program,
+ * or can be launched from command line to execute single help action.
+ *
+ * Usage as a Java component:
+ * <ul>
+ * <li> create an instantance of this class and then hold onto
+ * this instance for the duration of your application</li>
+ * <li> call start() </li>
+ * <li> call displayHelp(...) or displayContext(..) any number of times </li>
+ * <li> at the end, call shutdown(). </li>
+ * </ul>
+ */
+public class StandaloneHelp extends EclipseController {
+ // timout for .hostport file to apper since starting eclipse [ms]
+ private static final int STARTUP_TIMEOUT = 40 * 1000;
+ // number of retries to connectect to webapp
+ private static final int CONNECTION_RETRIES = 3;
+ // time between retries to connectect to webapp [ms]
+ private static final int CONNECTION_RETRY_INTERVAL = 5 * 1000;
+ // ID of the application to run
+ private static final String HELP_APPLICATION_ID =
+ HelpBasePlugin.PLUGIN_ID+".helpApplication";
+
+ /**
+ * Constructs help system
+ * @param args array of String options and their values
+ * Option <code>-eclipseHome dir</code> specifies Eclipse
+ * installation directory.
+ * It must be provided, when current directory is not the same
+ * as Eclipse installation directory.
+ * Additionally, most options accepted by Eclipse execuable are supported.
+ * @param applicationID ID of Eclipse help application
+ */
+ public StandaloneHelp(String[] args) {
+ super(HELP_APPLICATION_ID, args);
+ }
+
+ /**
+ * @see org.eclipse.help.standalone.Infocenter#main(String[])
+ */
+ public static void main(String[] args) {
+ try {
+ StandaloneHelp help = new StandaloneHelp(args);
+
+ List helpCommand = Options.getHelpCommand();
+
+ if (help.executeCommand(helpCommand)) {
+ return;
+ } else {
+ printMainUsage();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Overrides the initialization of the connection to pass retry parameters.
+ */
+ protected EclipseConnection initConnection() {
+ int timeout = STARTUP_TIMEOUT;
+ if (Options.getServerTimeout() > 0) {
+ timeout = 1000 * Options.getServerTimeout();
+ }
+ return new EclipseConnection(
+ timeout,
+ CONNECTION_RETRIES,
+ CONNECTION_RETRY_INTERVAL);
+ }
+ /**
+ * @see org.eclipse.help.standalone.Help#displayContext(java.lang.String,int,int)
+ */
+ public void displayContext(String contextId, int x, int y) {
+ }
+
+ /**
+ * @see org.eclipse.help.standalone.Help#displayContextInfopop(java.lang.String,int,int)
+ */
+ public void displayContextInfopop(String contextId, int x, int y) {
+ }
+ /**
+ * @see org.eclipse.help.standalone.Help#displayHelp()
+ */
+ public void displayHelp() throws Exception {
+ sendHelpCommand("displayHelp", new String[0]);
+ }
+
+ /**
+ * @see org.eclipse.help.standalone.Help#displayHelp(java.lang.String)
+ */
+ public void displayHelp(String href) throws Exception {
+ sendHelpCommand("displayHelp", new String[] { "href=" + href });
+ }
+
+ /**
+ * @return true if commands contained a known command
+ * and it was executed
+ */
+ private boolean executeCommand(List helpCommands) throws Exception {
+
+ if (helpCommands.size() <= 0) {
+ return false;
+ }
+ String command = (String) helpCommands.get(0);
+
+ if ("start".equalsIgnoreCase(command)) {
+ start();
+ return true;
+ } else if ("shutdown".equalsIgnoreCase(command)) {
+ shutdown();
+ return true;
+ } else if ("displayHelp".equalsIgnoreCase(command)) {
+ if (helpCommands.size() >= 2) {
+ displayHelp((String) helpCommands.get(1));
+ } else {
+ displayHelp();
+ }
+ return true;
+ } else if ("displayContext".equalsIgnoreCase(command)) {
+ if (helpCommands.size() >= 4) {
+ displayContext(
+ (String) helpCommands.get(1),
+ Integer.parseInt((String) helpCommands.get(2)),
+ Integer.parseInt((String) helpCommands.get(3)));
+
+ return true;
+ }
+ } else if ("displayContextInfopop".equalsIgnoreCase(command)) {
+ if (helpCommands.size() >= 4) {
+ displayContextInfopop(
+ (String) helpCommands.get(1),
+ Integer.parseInt((String) helpCommands.get(2)),
+ Integer.parseInt((String) helpCommands.get(3)));
+ return true;
+ }
+ }
+
+ return false;
+ }
+ /**
+ * Prints usage of this class as a program.
+ */
+ private static void printMainUsage() {
+ System.out.println("Parameters syntax:");
+ System.out.println();
+ System.out.println(
+ "-command start | shutdown | (displayHelp [href]) [-eclipsehome eclipseInstallPath] [-host helpServerHost] [-port helpServerPort] [platform options] [-vmargs [Java VM arguments]]");
+ System.out.println();
+ System.out.println("where:");
+ System.out.println(" href is the URL of the help resource to display,");
+ System.out.println(
+ " eclipseInstallPath specifies Eclipse installation directory; this directory is a parent to \"plugins\" directory and eclipse executable; the option must be provided, when current directory from which infocenter is launched, is not the same as Eclipse installation directory,");
+ System.out.println(
+ " helpServerHost specifies host name of the interface that help server will use,");
+ System.out.println(
+ " helpServerPort specifies port number that help server will use,");
+ System.out.println(
+ " platform options are other options that are supported by Eclipse Executable.");
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneInfocenter.java b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneInfocenter.java
new file mode 100644
index 000000000..dd132bd55
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/standalone/StandaloneInfocenter.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.standalone;
+
+import java.util.List;
+
+import org.eclipse.help.internal.HelpBasePlugin;
+
+/**
+ * This program is used to start or stop Eclipse
+ * Infocenter application.
+ */
+public class StandaloneInfocenter extends EclipseController {
+ // ID of the application to run
+ private static final String INFOCENTER_APPLICATION_ID =
+ HelpBasePlugin.PLUGIN_ID + ".infocenterApplication";
+
+ /**
+ * Constructs help system
+ * @param args array of String options and their values
+ * Option <code>-eclipseHome dir</code> specifies Eclipse
+ * installation directory.
+ * It must be provided, when current directory is not the same
+ * as Eclipse installation directory.
+ * Additionally, most options accepted by Eclipse execuable are supported.
+ * @param applicationID ID of Eclipse help application
+ */
+ public StandaloneInfocenter(String[] args) {
+ super(INFOCENTER_APPLICATION_ID, args);
+ }
+
+ /**
+ * @see org.eclipse.help.standalone.Infocenter#main(String[])
+ */
+ public static void main(String[] args) {
+ try {
+ StandaloneInfocenter infocenter = new StandaloneInfocenter(args);
+
+ List helpCommand = Options.getHelpCommand();
+
+ if (infocenter.executeCommand(helpCommand)) {
+ return;
+ } else {
+ printMainUsage();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @return true if commands contained a known command
+ * and it was executed
+ */
+ private boolean executeCommand(List helpCommand) throws Exception {
+ if (helpCommand.size() <= 0) {
+ return false;
+ }
+ String command = (String) helpCommand.get(0);
+ if ("start".equalsIgnoreCase(command)) {
+ start();
+ return true;
+ } else if ("shutdown".equalsIgnoreCase(command)) {
+ shutdown();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Prints usage of this class as a program.
+ */
+ private static void printMainUsage() {
+ System.out.println("Parameters syntax:");
+ System.out.println();
+ System.out.println(
+ "-command start | shutdown [-eclipsehome eclipseInstallPath] [-host helpServerHost] [-port helpServerPort] [-noexec] [platform options] [-vmargs [Java VM arguments]]");
+ System.out.println();
+ System.out.println("where:");
+ System.out.println(
+ " eclipseInstallPath specifies Eclipse installation directory; this directory is a parent to \"plugins\" directory and eclipse executable; the option must be provided, when current directory from which infocenter is launched, is not the same as Eclipse installation directory,");
+ System.out.println(
+ " helpServerHost specifies host name of the interface that help server will use,");
+ System.out.println(
+ " helpServerPort specifies port number that help server will use,");
+ System.out.println(
+ " noexec option indicates that Eclipse executable should not be used, ");
+ System.out.println(
+ " platform options are other options that are supported by Eclipse Executable.");
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/util/PluginVersionInfo.java b/org.eclipse.help.base/src/org/eclipse/help/internal/util/PluginVersionInfo.java
new file mode 100644
index 000000000..087e1c9de
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/util/PluginVersionInfo.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.util;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.model.*;
+import org.eclipse.help.internal.HelpBasePlugin;
+import org.eclipse.help.internal.util.*;
+/**
+ * Table of plugins. Records all plugins, their version, corresponding fragments versions
+ * The values are String in format:
+ * pluginID\npluginVersion\nfragment1ID\nfragment1Version\nfragment2ID\nfragment2Version
+ */
+public class PluginVersionInfo extends HelpProperties {
+ // Separates plugins and versions in value strings
+ static final String SEPARATOR = "\n";
+ Plugin basePlugin = HelpBasePlugin.getDefault();
+ boolean doComparison = true;
+ boolean hasChanged = false;
+ boolean ignoreSavedVersions;
+ Collection added = new ArrayList();
+ Collection removed = new ArrayList();
+ /**
+ * Creates table of current contributing plugins and their fragments with versions.
+ * @param name the name of the file to serialize the data to
+ * @param it iterator of current contributions (IConfigurationElement type)
+ * @param basePlugin use this plugin's state location to store the data
+ * @param ignoreSavedVersion if true, will cause detect change
+ * to ignore saved plugin version and behave like there was nothing saved
+ */
+ public PluginVersionInfo(
+ String name,
+ Iterator it,
+ Plugin basePlugin,
+ boolean ignoreSavedVersions) {
+ super(name, basePlugin);
+ this.basePlugin = basePlugin;
+ this.ignoreSavedVersions = ignoreSavedVersions;
+ if (it == null)
+ return;
+ // create table of current contributions
+ for (; it.hasNext();) {
+ IPluginDescriptor plugin = (IPluginDescriptor) it.next();
+ StringBuffer pluginVersionAndFragments = new StringBuffer();
+ pluginVersionAndFragments.append(plugin.getUniqueIdentifier());
+ pluginVersionAndFragments.append(SEPARATOR);
+ pluginVersionAndFragments.append(
+ plugin.getVersionIdentifier().toString());
+ if (plugin instanceof PluginDescriptorModel) {
+ PluginFragmentModel[] fragmentModels =
+ ((PluginDescriptorModel) plugin).getFragments();
+ if (fragmentModels != null) {
+ for (int f = 0; f < fragmentModels.length; f++) {
+ pluginVersionAndFragments.append(SEPARATOR);
+ pluginVersionAndFragments.append(
+ fragmentModels[f].getId());
+ pluginVersionAndFragments.append(SEPARATOR);
+ pluginVersionAndFragments.append(
+ fragmentModels[f].getVersion());
+ }
+ }
+ }
+ this.put(
+ plugin.getUniqueIdentifier(),
+ pluginVersionAndFragments.toString());
+
+ }
+ }
+ /**
+ * Detects changes in contributions or their version
+ * since last time the contribution table was saved.
+ * @return true if contributions have changed
+ */
+ public boolean detectChange() {
+ if (!doComparison)
+ return hasChanged;
+ // Create table of contributions present before last save()
+ HelpProperties oldContrs = new HelpProperties(this.name, basePlugin);
+ if (!ignoreSavedVersions) {
+ oldContrs.restore();
+ }
+ // check if contributions changed
+ hasChanged = false;
+ for (Enumeration keysEnum = this.keys(); keysEnum.hasMoreElements();) {
+ String oneContr = (String) keysEnum.nextElement();
+ if (!oldContrs.containsKey(oneContr)) {
+ // plugin has been added
+ added.add(oneContr);
+ } else {
+ String versions = (String) this.get(oneContr);
+ String oldVersions = (String) oldContrs.get(oneContr);
+ if (!compare(versions, oldVersions)) {
+ // plugin version changed or fragments changed
+ added.add(oneContr);
+ }
+ }
+ }
+ for (Enumeration keysEnum = oldContrs.keys();
+ keysEnum.hasMoreElements();
+ ) {
+ String oneContr = (String) keysEnum.nextElement();
+ if (!this.containsKey(oneContr)) {
+ // plugin has been removed
+ removed.add(oneContr);
+ } else {
+ String versions = (String) this.get(oneContr);
+ String oldVersions = (String) oldContrs.get(oneContr);
+ if (!compare(versions, oldVersions)) {
+ // plugin version changed or fragments changed
+ removed.add(oneContr);
+ }
+ }
+ }
+ hasChanged = added.size() > 0 || removed.size() > 0;
+ doComparison = false;
+ return hasChanged;
+ }
+ /**
+ * @return String - Collection of IDs of contributions that were added
+ * or upgraded
+ */
+ public Collection getAdded() {
+ if (doComparison)
+ detectChange();
+ return added;
+ }
+ /**
+ * @return String - Collection of IDs of contributions that were removed
+ * or upgraded
+ */
+ public Collection getRemoved() {
+ if (doComparison)
+ detectChange();
+ return removed;
+ }
+ /**
+ * Saves contributions to a file.
+ * After this method is called, calls to detectChange() will return false.
+ * @return true if operation was successful
+ */
+ public boolean save() {
+ if (super.save()) {
+ doComparison = false;
+ hasChanged = false;
+ ignoreSavedVersions = false;
+ added = new ArrayList();
+ removed = new ArrayList();
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Compares plugins and versions represented as a string for equality
+ * String have form id1\nverison1\nid2\nversion2
+ * String are equal of they contain the same set of IDs and their corresponding version equal
+ * @return true if plugins and versions match
+ */
+ private boolean compare(String versions, String oldVersions) {
+ Map versionMap = new HashMap();
+ for (StringTokenizer t =
+ new StringTokenizer(versions, SEPARATOR, false);
+ t.hasMoreTokens();
+ ) {
+ String pluginOrFragment = t.nextToken();
+ if (t.hasMoreTokens()) {
+ versionMap.put(pluginOrFragment, t.nextToken());
+ }
+ }
+ Map oldVersionMap = new HashMap();
+ for (StringTokenizer t =
+ new StringTokenizer(oldVersions, SEPARATOR, false);
+ t.hasMoreTokens();
+ ) {
+ String pluginOrFragment = t.nextToken();
+ if (t.hasMoreTokens()) {
+ oldVersionMap.put(pluginOrFragment, t.nextToken());
+ }
+ }
+ return versionMap.equals(oldVersionMap);
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableHelpResource.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableHelpResource.java
new file mode 100644
index 000000000..dcee80007
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableHelpResource.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.*;
+import org.w3c.dom.Element;
+
+/**
+ * Makes help resources adaptable and persistable
+ */
+public abstract class AdaptableHelpResource implements IAdaptable, IHelpResource {
+ protected IHelpResource element;
+ protected IAdaptable parent;
+
+ /**
+ * This constructor will be called when wrapping help resources.
+ */
+ public AdaptableHelpResource(IHelpResource element) {
+ this.element = element;
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IHelpResource.class)
+ return element;
+ else
+ return null;
+ }
+
+ public abstract void saveState(Element element);
+
+ public abstract AdaptableHelpResource[] getChildren();
+
+ public IAdaptable getParent() {
+ return parent;
+ }
+
+ protected void setParent(IAdaptable parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Tests the receiver and the object for equality
+ *
+ * @param object object to compare the receiver to
+ * @return true=the object equals the receiver, the name is the same.
+ * false otherwise
+ */
+ public boolean equals(Object object) {
+ if (this == object)
+ return true;
+ else if (object instanceof AdaptableHelpResource)
+ return (element == ((AdaptableHelpResource)object).element);
+ else if (object instanceof IHelpResource)
+ return element == object;
+ else
+ return false;
+ }
+
+ /**
+ * Returns the hash code.
+ *
+ * @return the hash code.
+ */
+ public int hashCode() {
+ if (element == null)
+ return -1;
+ else
+ return element.hashCode();
+ }
+
+ /**
+ * Returns a descendant topic with a specified href
+ */
+ public abstract ITopic getTopic(String href);
+
+ /**
+ * @see org.eclipse.help.IHelpResource#getHref()
+ */
+ public String getHref() {
+ return element.getHref();
+ }
+
+ /**
+ * @see org.eclipse.help.IHelpResource#getLabel()
+ */
+ public String getLabel() {
+ return element.getLabel();
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableToc.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableToc.java
new file mode 100644
index 000000000..fb1627714
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableToc.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+
+import org.eclipse.help.*;
+import org.w3c.dom.Element;
+
+/**
+ * Makes help resources adaptable and persistable
+ */
+public class AdaptableToc extends AdaptableHelpResource {
+
+ protected AdaptableTopic[] children;
+
+ /**
+ * This constructor will be called when wrapping help resources.
+ */
+ AdaptableToc(IToc element) {
+ super(element);
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IToc.class)
+ return element;
+ else
+ return super.getAdapter(adapter);
+ }
+
+ public AdaptableHelpResource[] getChildren() {
+ if (children == null) {
+ ITopic[] topics = ((IToc)element).getTopics();
+ children = new AdaptableTopic[topics.length];
+ for (int i = 0; i < topics.length; i++) {
+ children[i] = new AdaptableTopic(topics[i]);
+ children[i].setParent(this);
+ }
+ }
+ return children;
+ }
+
+ /**
+ * @see org.eclipse.help.IToc#getTopic(java.lang.String)
+ */
+ public ITopic getTopic(String href) {
+ return ((IToc)element).getTopic(href);
+ }
+
+ /**
+ * @see org.eclipse.help.IToc#getTopics()
+ */
+ public ITopic[] getTopics() {
+ return ((IToc)element).getTopics();
+ }
+
+ public void saveState(Element element) {
+ element.setAttribute("toc", getHref());
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTocsArray.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTocsArray.java
new file mode 100644
index 000000000..2e47d9e23
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTocsArray.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+
+import java.util.*;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.help.IToc;
+
+/**
+ * Makes help resources adaptable and persistable
+ */
+public class AdaptableTocsArray implements IAdaptable {
+
+ IToc[] element;
+ AdaptableToc[] children;
+ HashMap map;
+
+ /**
+ * This constructor will be called when wrapping help resources.
+ */
+ public AdaptableTocsArray(IToc[] tocs) {
+ this.element = tocs;
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IToc[].class)
+ return element;
+ else
+ return null;
+ }
+
+ public IAdaptable[] getChildren() {
+
+ if (children == null) {
+ children = new AdaptableToc[element.length];
+ for (int i = 0; i < element.length; i++) {
+ children[i] = new AdaptableToc(element[i]);
+ children[i].setParent(this);
+ }
+ }
+ return children;
+
+ }
+
+ public AdaptableToc getAdaptableToc(String href) {
+ if (map == null) {
+ getChildren(); // make sure children are initialized
+ map = new HashMap(children.length);
+ for (int i=0; i<children.length; i++)
+ map.put(children[i].getHref(), children[i]);
+ }
+ return (AdaptableToc)map.get(href);
+ }
+
+ IToc[] asArray() {
+ return element;
+ }
+
+ /**
+ * Tests the receiver and the object for equality
+ *
+ * @param object object to compare the receiver to
+ * @return true=the object equals the receiver, the name is the same.
+ * false otherwise
+ */
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (!(object instanceof AdaptableTocsArray)) {
+ return false;
+ }
+
+ AdaptableTocsArray res = (AdaptableTocsArray) object;
+ return (Arrays.equals(asArray(), res.asArray()));
+
+ }
+
+ /**
+ * Returns the hash code.
+ *
+ * @return the hash code.
+ */
+ public int hashCode() {
+ if (element == null)
+ return -1;
+ else
+ return element.hashCode();
+ }
+}
+
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTopic.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTopic.java
new file mode 100644
index 000000000..a3c9ba4d5
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/AdaptableTopic.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+
+import java.util.*;
+import java.util.Map;
+
+import org.eclipse.help.*;
+import org.eclipse.help.internal.util.FastStack;
+import org.w3c.dom.Element;
+
+/**
+ * Makes help resources adaptable and persistable
+ */
+public class AdaptableTopic extends AdaptableHelpResource {
+ /**
+ * Map of all topics with this topic as ancestor
+ */
+ private Map topicMap;
+
+ /**
+ * This constructor will be called when wrapping help resources.
+ */
+ AdaptableTopic(ITopic element) {
+ super(element);
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == ITopic.class)
+ return element;
+ else
+ return super.getAdapter(adapter);
+ }
+
+ public AdaptableHelpResource[] getChildren() {
+ return new AdaptableHelpResource[0];
+ }
+
+ /**
+ * @see org.eclipse.help.ITopic#getSubtopics()
+ */
+ public ITopic[] getSubtopics() {
+ return ((ITopic)element).getSubtopics();
+ }
+
+ /**
+ * Returns a topic with the specified href.
+ * <br> It is possible that multiple tocs have
+ * the same href, in which case there is no guarantee
+ * which one is returned.
+ * @param href The topic's href value.
+ */
+ public ITopic getTopic(String href) {
+ if (href == null)
+ return null;
+
+ if (topicMap == null) {
+ // traverse TOC and fill in the topicMap
+ topicMap = new HashMap();
+ topicMap.put(getHref(),element);
+ FastStack stack = new FastStack();
+ ITopic[] topics = getSubtopics();
+ for (int i = 0; i < topics.length; i++)
+ stack.push(topics[i]);
+ while (!stack.isEmpty()) {
+ ITopic topic = (ITopic) stack.pop();
+ if (topic != null) {
+ String topicHref = topic.getHref();
+ if (topicHref != null) {
+ topicMap.put(topicHref, topic);
+ }
+ ITopic[] subtopics = topic.getSubtopics();
+ for (int i = 0; i < subtopics.length; i++)
+ stack.push(subtopics[i]);
+ }
+ }
+ }
+ return (ITopic) topicMap.get(href);
+ }
+
+ public void saveState(Element element) {
+ AdaptableToc toc = (AdaptableToc)getParent();
+ toc.saveState(element);
+ AdaptableHelpResource[] topics = toc.getChildren();
+ for (int i=0; i<topics.length; i++)
+ if (topics[i] == this)
+ element.setAttribute("topic", String.valueOf(i));
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/IHelpWorkingSetManager.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/IHelpWorkingSetManager.java
new file mode 100644
index 000000000..b95f74953
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/IHelpWorkingSetManager.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+import java.io.*;
+
+/**
+ * The working set manager stores help working sets. Working sets are persisted
+ * whenever one is added or removed.
+ * @since 3.0
+ */
+public interface IHelpWorkingSetManager {
+
+ public AdaptableTocsArray getRoot();
+ /**
+ * Adds a new working set and saves it
+ */
+ public void addWorkingSet(WorkingSet workingSet) throws IOException;
+
+ /**
+ * Creates a new working set
+ */
+ public WorkingSet createWorkingSet(
+ String name,
+ AdaptableHelpResource[] elements);
+
+ /**
+ * Returns a working set by name
+ *
+ */
+ public WorkingSet getWorkingSet(String name);
+
+ /**
+ * Implements IWorkingSetManager.
+ *
+ * @see org.eclipse.ui.IWorkingSetManager#getWorkingSets()
+ */
+ public WorkingSet[] getWorkingSets();
+
+ /**
+ * Removes specified working set
+ */
+ public void removeWorkingSet(WorkingSet workingSet);
+
+ /**
+ * Persists all working sets. Should only be called by the webapp working
+ * set dialog.
+ *
+ * @param changedWorkingSet the working set that has changed
+ */
+ public void workingSetChanged(WorkingSet changedWorkingSet)
+ throws IOException;
+
+ public AdaptableToc getAdaptableToc(String href);
+
+ public AdaptableTopic getAdaptableTopic(String id);
+
+ public String getCurrentWorkingSet();
+
+ public void setCurrentWorkingSet(String workingSet);
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/PropertyChange.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/PropertyChange.java
new file mode 100644
index 000000000..3575b9f9f
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/PropertyChange.java
@@ -0,0 +1,306 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+import java.util.*;
+
+/**
+ * Utility classes copied from the org.eclipse.core.runtime.Preferences class.
+ */
+public class PropertyChange {
+
+
+ /**
+ * Listener for property changes.
+ * <p>
+ * Usage:
+ * <pre>
+ * Preferences.IPropertyChangeListener listener =
+ * new Preferences.IPropertyChangeListener() {
+ * public void propertyChange(Preferences.PropertyChangeEvent event) {
+ * ... // code to deal with occurrence of property change
+ * }
+ * };
+ * emitter.addPropertyChangeListener(listener);
+ * ...
+ * emitter.removePropertyChangeListener(listener);
+ * </pre>
+ * </p>
+ */
+ public interface IPropertyChangeListener extends EventListener {
+
+ /**
+ * Notification that a property has changed.
+ * <p>
+ * This method gets called when the observed object fires a property
+ * change event.
+ * </p>
+ *
+ * @param event the property change event object describing which
+ * property changed and how
+ */
+ public void propertyChange(PropertyChangeEvent event);
+ }
+
+ /**
+ * An event object describing a change to a named property.
+ * <p>
+ * The preferences object reports property change events for internal state
+ * changes that may be of interest to external parties. A special listener
+ * interface (<code>Preferences.IPropertyChangeListener</code>) is
+ * defined for this purpose. Listeners are registered via the
+ * <code>Preferences.addPropertyChangeListener</code> method.
+ * </p>
+ * <p>
+ * Clients cannot instantiate or subclass this class.
+ * </p>
+ *
+ * @see #addPropertyChangeListener
+ * @see #IPropertyChangeListener
+ */
+ public static class PropertyChangeEvent extends EventObject {
+
+ /**
+ * The name of the changed property.
+ */
+ private String propertyName;
+
+ /**
+ * The old value of the changed property, or <code>null</code> if
+ * not known or not relevant.
+ */
+ private Object oldValue;
+
+ /**
+ * The new value of the changed property, or <code>null</code> if
+ * not known or not relevant.
+ */
+ private Object newValue;
+
+ /**
+ * Creates a new property change event.
+ *
+ * @param source the object whose property has changed
+ * @param property the property that has changed (must not be
+ * <code>null</code>)
+ * @param oldValue the old value of the property, or
+ * <code>null</code> if none
+ * @param newValue the new value of the property, or
+ * <code>null</code> if none
+ */
+ PropertyChangeEvent(
+ Object source,
+ String property,
+ Object oldValue,
+ Object newValue) {
+
+ super(source);
+ if (property == null) {
+ throw new IllegalArgumentException();
+ }
+ this.propertyName = property;
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ }
+
+ /**
+ * Returns the name of the property that changed.
+ * <p>
+ * Warning: there is no guarantee that the property name returned
+ * is a constant string. Callers must compare property names using
+ * <code>equals</code>, not ==.
+ *</p>
+ *
+ * @return the name of the property that changed
+ */
+ public String getProperty() {
+ return propertyName;
+ }
+
+ /**
+ * Returns the new value of the property.
+ *
+ * @return the new value, or <code>null</code> if not known
+ * or not relevant
+ */
+ public Object getNewValue() {
+ return newValue;
+ }
+
+ /**
+ * Returns the old value of the property.
+ *
+ * @return the old value, or <code>null</code> if not known
+ * or not relevant
+ */
+ public Object getOldValue() {
+ return oldValue;
+ }
+ }
+
+ /**
+ * Internal class is used to maintain a list of listeners.
+ * It is a fairly lightweight object, occupying minimal space when
+ * no listeners are registered.
+ * <p>
+ * Note that the <code>add</code> method checks for and eliminates
+ * duplicates based on identity (not equality). Likewise, the
+ * <code>remove</code> method compares based on identity.
+ * </p>
+ * <p>
+ * Use the <code>getListeners</code> method when notifying listeners.
+ * Note that no garbage is created if no listeners are registered.
+ * The recommended code sequence for notifying all registered listeners
+ * of say, <code>FooListener.eventHappened</code>, is:
+ * <pre>
+ * Object[] listeners = myListenerList.getListeners();
+ * for (int i = 0; i &lt; listeners.length; ++i) {
+ * ((FooListener) listeners[i]).eventHappened(event);
+ * }
+ * </pre>
+ * </p>
+ */
+ public static class ListenerList {
+ /**
+ * The initial capacity of the list. Always >= 1.
+ */
+ private int capacity;
+
+ /**
+ * The current number of listeners.
+ * Maintains invariant: 0 <= size <= listeners.length.
+ */
+ private int size;
+
+ /**
+ * The list of listeners. Initially <code>null</code> but initialized
+ * to an array of size capacity the first time a listener is added.
+ * Maintains invariant: listeners != null IFF size != 0
+ */
+ private Object[] listeners = null;
+
+ /**
+ * The empty array singleton instance, returned by getListeners()
+ * when size == 0.
+ */
+ private static final Object[] EmptyArray = new Object[0];
+
+ /**
+ * Creates a listener list with an initial capacity of 3.
+ */
+ public ListenerList() {
+ this(3);
+ }
+
+ /**
+ * Creates a listener list with the given initial capacity.
+ *
+ * @param capacity the number of listeners which this list can initially
+ * accept without growing its internal representation; must be at
+ * least 1
+ */
+ public ListenerList(int capacity) {
+ if (capacity < 1) {
+ throw new IllegalArgumentException();
+ }
+ this.capacity = capacity;
+ }
+
+ /**
+ * Adds the given listener to this list. Has no effect if an identical
+ * listener is already registered.
+ *
+ * @param listener the listener
+ */
+ public void add(Object listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+ if (size == 0) {
+ listeners = new Object[capacity];
+ } else {
+ // check for duplicates using identity
+ for (int i = 0; i < size; ++i) {
+ if (listeners[i] == listener) {
+ return;
+ }
+ }
+ // grow array if necessary
+ if (size == listeners.length) {
+ System.arraycopy(listeners, 0, listeners = new Object[size * 2 + 1], 0, size);
+ }
+ }
+ listeners[size++] = listener;
+ }
+
+ /**
+ * Returns an array containing all the registered listeners.
+ * The resulting array is unaffected by subsequent adds or removes.
+ * If there are no listeners registered, the result is an empty array
+ * singleton instance (no garbage is created).
+ * Use this method when notifying listeners, so that any modifications
+ * to the listener list during the notification will have no effect on
+ * the notification itself.
+ *
+ * @return the list of registered listeners
+ */
+ public Object[] getListeners() {
+ if (size == 0)
+ return EmptyArray;
+ Object[] result = new Object[size];
+ System.arraycopy(listeners, 0, result, 0, size);
+ return result;
+ }
+
+ /**
+ * Returns whether this listener list is empty.
+ *
+ * @return <code>true</code> if there are no registered listeners, and
+ * <code>false</code> otherwise
+ */
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
+ * Removes the given listener from this list. Has no effect if an
+ * identical listener was not already registered.
+ *
+ * @param listener the listener
+ */
+ public void remove(Object listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+ for (int i = 0; i < size; ++i) {
+ if (listeners[i] == listener) {
+ if (size == 1) {
+ listeners = null;
+ size = 0;
+ } else {
+ System.arraycopy(listeners, i + 1, listeners, i, --size - i);
+ listeners[size] = null;
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Returns the number of registered listeners.
+ *
+ * @return the number of registered listeners
+ */
+ public int size() {
+ return size;
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSet.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSet.java
new file mode 100644
index 000000000..57fe978b3
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSet.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+import java.util.*;
+
+import org.w3c.dom.*;
+
+public class WorkingSet {
+ private String name;
+ private List elements;
+
+ public WorkingSet(String name) {
+ this(name, (List)null);
+ }
+
+ public WorkingSet(String name, List elements) {
+ this.name = name;
+ if (elements == null)
+ elements = new ArrayList();
+
+ this.elements = elements;
+ }
+
+ public WorkingSet(String name, AdaptableHelpResource[] elements) {
+ this.name = name;
+ if (elements == null)
+ elements = new AdaptableHelpResource[0];
+
+ this.elements = new ArrayList(elements.length);
+ for (int i=0; i<elements.length; i++) {
+ this.elements.add(elements[i]);
+ }
+ }
+
+ public void removeElement(AdaptableHelpResource element) {
+ // Note: this is based on equality of IHelpResource and AdaptableHelpResource
+ elements.remove(element);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String newName) {
+ if (newName == null)
+ return;
+ name = newName;
+ }
+
+ public AdaptableHelpResource[] getElements() {
+ AdaptableHelpResource[] array = new AdaptableHelpResource[elements.size()];
+ elements.toArray(array);
+ return array;
+ }
+
+ public void setElements(AdaptableHelpResource[] elements) {
+ this.elements = new ArrayList(elements.length);
+ for (int i=0; i<elements.length; i++)
+ this.elements.add(elements[i]);
+ }
+
+ public void saveState(Element parent) {
+ Document doc = parent.getOwnerDocument();
+ Element ws = doc.createElement("workingSet");
+ ws.setAttribute("name", name);
+ parent.appendChild(ws);
+
+ for (Iterator it=elements.iterator(); it.hasNext(); ) {
+ Element child = doc.createElement("item");
+ AdaptableHelpResource helpResource = (AdaptableHelpResource)it.next();
+ helpResource.saveState(child);
+ ws.appendChild(child);
+ }
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetComparator.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetComparator.java
new file mode 100644
index 000000000..5c92986a9
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetComparator.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+
+import java.text.Collator;
+import java.util.Comparator;
+
+/**
+ * Compares two working sets by name.
+ */
+public class WorkingSetComparator implements Comparator {
+ private Collator fCollator = Collator.getInstance();
+
+ /**
+ * Implements Comparator.
+ *
+ * @see Comparator#compare(Object, Object)
+ */
+ public int compare(Object o1, Object o2) {
+ String name1 = null;
+ String name2 = null;
+
+ if (o1 instanceof WorkingSet)
+ name1 = ((WorkingSet) o1).getName();
+
+ if (o2 instanceof WorkingSet)
+ name2 = ((WorkingSet) o2).getName();
+
+ if (name1 == null || name2 == null)
+ return -1;
+
+ return fCollator.compare(name1, name2);
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetManager.java b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetManager.java
new file mode 100644
index 000000000..225e1257c
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/internal/workingset/WorkingSetManager.java
@@ -0,0 +1,479 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.internal.workingset;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.xerces.dom.*;
+import org.apache.xerces.parsers.*;
+import org.apache.xml.serialize.*;
+import org.eclipse.core.boot.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.help.internal.*;
+import org.eclipse.help.internal.util.*;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+/**
+ * The working set manager stores help working sets. Working sets are persisted
+ * whenever one is added or removed.
+ * @since 2.1
+ */
+public class WorkingSetManager implements IHelpWorkingSetManager {
+
+ // Note: keep the following constants in sych with the values defined in IWorkingSetManager.
+ // They are needed to synch the ui and the help working sets, as help should run w/o ui plugins.
+
+ /**
+ * Change event id when a working set is added
+ * newValue of the PropertyChangeEvent will be the added working set.
+ * oldValue will be null.
+ *
+ * @see IPropertyChangeListener
+ */
+ public static final String CHANGE_WORKING_SET_ADD = "workingSetAdd";
+ /**
+ * Change event id when a working set is removed
+ * newValue of the PropertyChangeEvent will be null.
+ * oldValue will be the removed working set.
+ *
+ * @see IPropertyChangeListener
+ */
+ public static final String CHANGE_WORKING_SET_REMOVE = "workingSetRemove";
+ /**
+ * Change event id when the working set contents changed
+ * newValue of the PropertyChangeEvent will be the changed working set.
+ * oldValue will be null.
+ *
+ * @see IPropertyChangeListener
+ */
+ public static final String CHANGE_WORKING_SET_CONTENT_CHANGE =
+ "workingSetContentChange";
+ /**
+ * Change event id when the working set name changed.
+ * newValue of the PropertyChangeEvent will be the changed working set.
+ * oldValue will be null.
+ *
+ * @see IPropertyChangeListener
+ */
+ public static final String CHANGE_WORKING_SET_NAME_CHANGE = "workingSetNameChange"; //$NON-NLS-1$
+
+ /**
+ * Synchronize event id. When other working sets repositories are used,
+ * one may want to keep things in synch.
+ *
+ * @see IPropertyChangeListener
+ */
+ public static final String CHANGE_WORKING_SETS_SYNCH = "workingSetsSynch";
+
+ // Working set persistence
+ private static final String WORKING_SET_STATE_FILENAME = "workingsets.xml";
+ private SortedSet workingSets = new TreeSet(new WorkingSetComparator());
+ private String locale;
+ private PropertyChange.ListenerList propertyChangeListeners =
+ new PropertyChange.ListenerList();
+ private AdaptableTocsArray root;
+
+ /**
+ * Constructor
+ * @param locale
+ */
+ public WorkingSetManager(String locale) {
+ this.locale = locale != null ? locale : BootLoader.getNL();
+ restoreState();
+ }
+
+ public AdaptableTocsArray getRoot() {
+ if (root == null)
+ root =
+ new AdaptableTocsArray(
+ HelpCore.getTocManager().getTocs(locale));
+ return root;
+ }
+
+ /**
+ * Adds a new working set and saves it
+ */
+ public void addWorkingSet(WorkingSet workingSet) {
+ if (workingSet == null || workingSets.contains(workingSet))
+ return;
+ workingSets.add(workingSet);
+ saveState();
+ firePropertyChange(CHANGE_WORKING_SET_ADD, null, workingSet);
+ }
+
+ /**
+ */
+ public void addPropertyChangeListener(
+ PropertyChange.IPropertyChangeListener listener) {
+ propertyChangeListeners.add(listener);
+ }
+
+ /**
+ * Creates a new working set
+ */
+ public WorkingSet createWorkingSet(
+ String name,
+ AdaptableHelpResource[] elements) {
+ return new WorkingSet(name, elements);
+ }
+
+ /**
+ * Tests the receiver and the object for equality
+ *
+ * @param object object to compare the receiver to
+ * @return true=the object equals the receiver, it has the same
+ * working sets. false otherwise
+ */
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof WorkingSetManager) {
+ WorkingSetManager workingSetManager = (WorkingSetManager) object;
+ return workingSetManager.workingSets.equals(workingSets);
+ }
+ return false;
+ }
+
+ /**
+ * Notify property change listeners about a change to the list of
+ * working sets.
+ *
+ * @param changeId one of
+ * CHANGE_WORKING_SET_ADD
+ * CHANGE_WORKING_SET_REMOVE
+ * CHANGE_WORKING_SET_CONTENT_CHANGE
+ * CHANGE_WORKING_SET_NAME_CHANGE
+ * @param oldValue the removed working set or null if a working set
+ * was added or changed.
+ * @param newValue the new or changed working set or null if a working
+ * set was removed.
+ */
+ private void firePropertyChange(
+ String changeId,
+ Object oldValue,
+ Object newValue) {
+ final PropertyChange.PropertyChangeEvent event =
+ new PropertyChange.PropertyChangeEvent(
+ this,
+ changeId,
+ oldValue,
+ newValue);
+
+ Object[] listeners = propertyChangeListeners.getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ (
+ (
+ PropertyChange
+ .IPropertyChangeListener) listeners[i])
+ .propertyChange(
+ event);
+ }
+ }
+ /**
+ * Returns a working set by name
+ *
+ */
+ public WorkingSet getWorkingSet(String name) {
+ if (name == null || workingSets == null)
+ return null;
+
+ Iterator iter = workingSets.iterator();
+ while (iter.hasNext()) {
+ WorkingSet workingSet = (WorkingSet) iter.next();
+ if (name.equals(workingSet.getName()))
+ return workingSet;
+ }
+ return null;
+ }
+ /**
+ * Returns the hash code.
+ *
+ * @return the hash code.
+ */
+ public int hashCode() {
+ return workingSets.hashCode();
+ }
+ /**
+ * Implements IWorkingSetManager.
+ *
+ * @see org.eclipse.ui.IWorkingSetManager#getWorkingSets()
+ */
+ public WorkingSet[] getWorkingSets() {
+ return (WorkingSet[]) workingSets.toArray(
+ new WorkingSet[workingSets.size()]);
+ }
+ /**
+ * Returns the file used as the persistence store
+ *
+ * @return the file used as the persistence store
+ */
+ private File getWorkingSetStateFile() {
+ IPath path = HelpBasePlugin.getDefault().getStateLocation();
+ path = path.append(locale);
+ path = path.append(WORKING_SET_STATE_FILENAME);
+ return path.toFile();
+ }
+
+ /**
+ * Removes specified working set
+ */
+ public void removeWorkingSet(WorkingSet workingSet) {
+ workingSets.remove(workingSet);
+ saveState();
+ firePropertyChange(CHANGE_WORKING_SET_REMOVE, workingSet, null);
+ }
+
+ /**
+ * Reads the persistence store and creates the working sets
+ * stored in it.
+ */
+ public boolean restoreState() {
+ File stateFile = getWorkingSetStateFile();
+
+ if (stateFile.exists()) {
+ try {
+ FileInputStream input = new FileInputStream(stateFile);
+ InputStreamReader reader = new InputStreamReader(input, "utf-8"); //$NON-NLS-1$
+
+ InputSource inputSource = new InputSource(reader);
+ inputSource.setSystemId(stateFile.toString());
+
+ DOMParser parser = new DOMParser();
+ parser.parse(inputSource);
+ if (parser.getDocument() == null)
+ return false;
+
+ Element rootElement = parser.getDocument().getDocumentElement();
+ restoreWorkingSetState(rootElement);
+ input.close();
+
+ return true;
+
+ } catch (SAXException se) {
+ String msg = Resources.getString("E018", stateFile.toString());
+ HelpBasePlugin.logError(msg, se);
+ HelpBasePlugin.logError(Resources.getString("E041"), se);
+ return false;
+ } catch (IOException ioe) {
+ String msg = Resources.getString("E018", stateFile.toString());
+ HelpBasePlugin.logError(msg, ioe);
+ HelpBasePlugin.logError(Resources.getString("E041"), ioe);
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Recreates all working sets from the persistence store
+ * and adds them to the receiver.
+ *
+ * @param parent the xml element containing serialized working sets
+ */
+ private void restoreWorkingSetState(Element parent) {
+ NodeList workingSets = parent.getChildNodes();
+
+ for (int i = 0; i < workingSets.getLength(); i++) {
+ if (workingSets.item(i).getNodeType() != Element.ELEMENT_NODE)
+ continue;
+
+ WorkingSet workingSet =
+ restoreWorkingSet((Element) workingSets.item(i));
+ if (workingSet != null) {
+ this.workingSets.add(workingSet);
+ }
+ }
+ }
+
+ /**
+ * Recreates a working set from the persistence store.
+ *
+ * @param memento the persistence store
+ * @return the working set created from the memento or null if
+ * creation failed.
+ */
+ private WorkingSet restoreWorkingSet(Element workingSetNode) {
+
+ String name = workingSetNode.getAttribute("name");
+ NodeList items = workingSetNode.getElementsByTagName("item");
+ List helpResources = new ArrayList(items.getLength());
+ for (int i = 0; i < items.getLength(); i++) {
+ Element item = (Element) items.item(i);
+ String href = item.getAttribute("toc");
+ if (href == null || href.length() == 0)
+ continue;
+
+ String child_pos = item.getAttribute("topic");
+ int pos = -1;
+ if (child_pos != null) {
+ try {
+ pos = Integer.parseInt(child_pos);
+ } catch (Exception e) {
+ }
+ }
+
+ AdaptableHelpResource toc = getAdaptableToc(href);
+
+ if (toc == null)
+ return null;
+
+ if (pos == -1) {
+ // Create the adaptable toc.
+ helpResources.add(toc);
+ } else {
+ // Create the adaptable topic
+ AdaptableTopic[] topics = (AdaptableTopic[]) toc.getChildren();
+ if (pos >= 0 && topics.length > pos)
+ helpResources.add(topics[pos]);
+ }
+ }
+
+ AdaptableHelpResource[] elements =
+ new AdaptableHelpResource[helpResources.size()];
+ helpResources.toArray(elements);
+
+ WorkingSet ws = createWorkingSet(name, elements);
+
+ return ws;
+ }
+
+ /**
+ */
+ public void removePropertyChangeListener(
+ PropertyChange.IPropertyChangeListener listener) {
+ propertyChangeListeners.remove(listener);
+ }
+
+ /**
+ * Saves the working sets in the persistence store
+ */
+ public synchronized boolean saveState() {
+ Document doc = new DocumentImpl();
+ Element rootElement = doc.createElement("workingSets");
+ doc.appendChild(rootElement);
+
+ saveWorkingSetState(rootElement);
+
+ File stateFile = getWorkingSetStateFile();
+ try {
+ stateFile.getParentFile().mkdir();
+ FileOutputStream stream = new FileOutputStream(stateFile);
+
+ OutputFormat format = new OutputFormat();
+ format.setEncoding("UTF-8");
+ Serializer serializer =
+ SerializerFactory.getSerializerFactory("xml").makeSerializer(
+ stream,
+ format);
+
+ serializer.asDOMSerializer().serialize(doc);
+ stream.close();
+ return true;
+ } catch (IOException e) {
+ stateFile.delete();
+ String message = Resources.getString("E40");
+ HelpBasePlugin.logError(message, null);
+ return false;
+ }
+ }
+
+ /**
+ * Saves all persistable working sets in the persistence store.
+ *
+ * @param parent: the xml node to save to
+ */
+ private void saveWorkingSetState(Element parent) {
+ Iterator iterator = workingSets.iterator();
+
+ while (iterator.hasNext()) {
+ WorkingSet workingSet = (WorkingSet) iterator.next();
+ workingSet.saveState(parent);
+ }
+ }
+ /**
+ * Persists all working sets. Should only be called by the webapp working
+ * set dialog.
+ *
+ * @param changedWorkingSet the working set that has changed
+ */
+ public void workingSetChanged(WorkingSet changedWorkingSet) {
+ saveState();
+ firePropertyChange(
+ CHANGE_WORKING_SET_NAME_CHANGE,
+ null,
+ changedWorkingSet);
+ firePropertyChange(
+ CHANGE_WORKING_SET_CONTENT_CHANGE,
+ null,
+ changedWorkingSet);
+ }
+
+ /**
+ * Synchronizes the working sets. Should only be called by the webapp
+ * working set manager dialog.
+ *
+ * @param changedWorkingSet the working set that has changed
+ */
+ public void synchronizeWorkingSets() {
+ firePropertyChange(CHANGE_WORKING_SETS_SYNCH, null, null);
+ }
+
+ public AdaptableToc getAdaptableToc(String href) {
+ return getRoot().getAdaptableToc(href);
+ }
+
+ public AdaptableTopic getAdaptableTopic(String id) {
+
+ if (id == null || id.length() == 0)
+ return null;
+
+ // toc id's are hrefs: /pluginId/path/to/toc.xml
+ // topic id's are based on parent toc id and index of topic: /pluginId/path/to/toc.xml_index_
+ int len = id.length();
+ if (id.charAt(len - 1) == '_') {
+ // This is a first level topic
+ String indexStr =
+ id.substring(id.lastIndexOf('_', len - 2) + 1, len - 1);
+ int index = 0;
+ try {
+ index = Integer.parseInt(indexStr);
+ } catch (Exception e) {
+ }
+
+ String tocStr = id.substring(0, id.lastIndexOf('_', len - 2));
+ AdaptableToc toc = getAdaptableToc(tocStr);
+ if (toc == null)
+ return null;
+ IAdaptable[] topics = toc.getChildren();
+ if (index < 0 || index >= topics.length)
+ return null;
+ else
+ return (AdaptableTopic) topics[index];
+ }
+
+ return null;
+ }
+ public String getCurrentWorkingSet() {
+ return HelpBasePlugin.getDefault().getPluginPreferences().getString(
+ HelpSystem.WORKING_SET);
+ }
+
+ public void setCurrentWorkingSet(String workingSet) {
+ HelpBasePlugin.getDefault().getPluginPreferences().setValue(
+ HelpSystem.WORKING_SET,
+ workingSet);
+ HelpBasePlugin.getDefault().savePluginPreferences();
+ }
+
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/standalone/Help.java b/org.eclipse.help.base/src/org/eclipse/help/standalone/Help.java
new file mode 100644
index 000000000..04043958b
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/standalone/Help.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.standalone;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import org.eclipse.help.internal.standalone.StandaloneHelp;
+
+/**
+ * This is a standalone help system. It takes care of
+ * launching the Eclipse with its help system implementation,
+ * and controling it.
+ * This class can be instantiated and used in a Java program,
+ * or can be launched from command line to execute single help action.
+ *
+ * Usage as a Java component:
+ * <ul>
+ * <li> create an instantance of this class and then hold onto
+ * this instance for the duration of your application</li>
+ * <li> call start() </li>
+ * <li> call displayHelp(...) or displayContext(..) any number of times </li>
+ * <li> at the end, call shutdown(). </li>
+ * </ul>
+ */
+public class Help {
+ private StandaloneHelp help;
+
+ /**
+ * Constructs help system
+ * @param options array of String options and their values
+ * <p>
+ * Option <code>-eclipseHome dir</code> specifies Eclipse
+ * installation directory. This directory is a parent to "plugins" directory
+ * and eclipse executable. The option must be provided, when current directory
+ * from which infocenter is launched, is
+ * not the same as Eclipse installation directory.
+ * <p>
+ * Option <code>-host helpServerHost</code> specifies host name
+ * of the interface that help server will use.
+ * It overrides host name specified in the application server plugin preferences.
+ * <p>
+ * Option <code>-port helpServerPort</code> specifies port number
+ * that help server will use.
+ * It overrides port number specified in the application server plugin preferences.
+ * <p>
+ * Option <code>-servertimeout timeout</code> number of seconds waiting
+ * to connect to the help server while executing commands, such as shutdown.
+ * shutdown. Default is 40s. You may need to use this option when
+ * running from a slow media such as a CD-ROM.
+ * <p>
+ * Additionally, most options accepted by Eclipse execuable are supported.
+ */
+ public Help(String[] options) {
+ help = new StandaloneHelp(options);
+ }
+ /**
+ * This contstructs the stand alone help.
+ * @param pluginsDir directory containing Eclipse plugins
+ * @deprecated use Help#Help(String[])
+ */
+ public Help(String pluginsDir) {
+ File plugins = new File(pluginsDir);
+ String install = plugins.getParent();
+ ArrayList options = new ArrayList(2);
+ if (install != null) {
+ options = new ArrayList(2);
+ options.add("-eclipseHome");
+ options.add(install);
+ }
+ String[] args = new String[options.size()];
+ options.toArray(args);
+ help = new StandaloneHelp(args);
+ }
+ /**
+ * Starts the stand alone help system.
+ */
+ public void start() throws Exception {
+ help.start();
+ }
+ /**
+ * Shuts-down the stand alone help system.
+ */
+ public void shutdown() throws Exception {
+ help.shutdown();
+ }
+ /**
+ * Displays help.
+ */
+ public void displayHelp() throws Exception {
+ help.displayHelp();
+ }
+
+ /**
+ * Displays specified help resource.
+ * @param href the href of the table of contents
+ */
+ public void displayHelp(String href) throws Exception {
+ help.displayHelp(href);
+ }
+
+ /**
+ * Displays context sensitive help.
+ * @param contextId context id
+ * @param x x coordinate
+ * @param y y coordinate
+ */
+ public void displayContext(String contextId, int x, int y)
+ throws Exception {
+ help.displayContext(contextId, x, y);
+ }
+
+ /**
+ * Displays context sensitive help in infopop.
+ * @param contextId context id
+ * @param x x coordinate
+ * @param y y coordinate
+ */
+ public void displayContextInfopop(String contextId, int x, int y)
+ throws Exception {
+ help.displayContextInfopop(contextId, x, y);
+ }
+
+ /**
+ * Controls standalone help system from command line.
+ * @param args array of String containing options
+ * Options are:
+ * <code>-command start | shutdown | (displayHelp [href]) [-eclipsehome eclipseInstallPath] [-host helpServerHost] [-port helpServerPort] [-servertimeout timeout] [platform options] [-vmargs JavaVMarguments]</code>
+ * where
+ * <ul>
+ * <li><code>href</code> is the URL of the help resource to display,</li>
+ * <li><code>eclipseInstallPath</code> specifies Eclipse installation directory;
+ * it must be provided, when current directory is not the same
+ * as Eclipse installation directory,</li>
+ * <li><code>helpServerHost</code> specifies host name of the interface
+ * that help server will use, it overrides host name specified
+ * the application server plugin preferences</li>
+ * <li><code>helpServerPort</code> specifies port number
+ * that help server will use, it overrides port number specified
+ * the application server plugin preferences.</li>
+ * <li><code>timeout</code> number of seconds waiting
+ * to connect to the help server while executing commands,
+ * such as shutdown. Default is 40s. You may need to
+ * use this option when running from a slow media such as a CD-ROM.
+ * <li><code>platform options</code> are other options that are supported by Eclipse Executable.</li>
+ * <ul>
+ */
+ public static void main(String[] args) {
+ StandaloneHelp.main(args);
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/standalone/Infocenter.java b/org.eclipse.help.base/src/org/eclipse/help/standalone/Infocenter.java
new file mode 100644
index 000000000..3b3a74b9f
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/standalone/Infocenter.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.help.standalone;
+
+import org.eclipse.help.internal.standalone.StandaloneInfocenter;
+
+/**
+ * This is a standalone infocenter. It takes care of
+ * launching the Eclipse with its help system implementation.
+ * This class can be instantiated and used in a Java program,
+ * or can be launched from command line.
+ *
+ * Usage as a Java component:
+ * <ul>
+ * <li> create an instantance of this class</li>
+ * <li> call start(), infocenter will run </li>
+ * <li> when no longer needed call shutdown(). </li>
+ * </ul>
+ */
+public class Infocenter {
+ private StandaloneInfocenter infocenter;
+ /**
+ * Constructs Infocenter
+ * @param options array of String options and their values
+ * <p>
+ * Option <code>-eclipseHome dir</code> specifies Eclipse
+ * installation directory. This directory is a parent to "plugins" directory
+ * and eclipse executable. The option must be provided, when current directory
+ * from which infocenter is launched, is
+ * not the same as Eclipse installation directory.
+ * <p>
+ * Option <code>-host helpServerHost</code> specifies host name
+ * of the interface that help server will use.
+ * It overrides host name specified in the application server plugin preferences.
+ * <p>
+ * Option <code>-port helpServerPort</code> specifies port number
+ * that help server will use.
+ * It overrides port number specified in the application server plugin preferences.
+ * <p>
+ * Option <code>-servertimeout timeout</code> number of seconds waiting
+ * to connect to the help server while executing commands, such as shutdown.
+ * shutdown. Default is 40s. You may need to use this option when
+ * running from a slow media such as a CD-ROM.
+ * <p>
+ * Option <code>-noexec</code> indicates that
+ * Eclipse executable should not be used.
+ * <p>
+ * Additionally, most options accepted by Eclipse execuable are supported.
+ */
+ public Infocenter(String[] options) {
+ infocenter = new StandaloneInfocenter(options);
+ }
+ /**
+ * Starts the stand alone infocenter.
+ */
+ public void start() throws Exception {
+ infocenter.start();
+ }
+ /**
+ * Shuts-down the stand alone infocenter.
+ */
+ public void shutdown() throws Exception {
+ infocenter.shutdown();
+ }
+ /**
+ * Controls start up and shut down of infocenter from command line.
+ * @param args array of String containing options
+ * Options are:
+ * <code>-command start | shutdown [-eclipsehome eclipseInstallPath] [-host helpServerHost] [-port helpServerPort] [-servertimeout timeout] [-noexec] [platform options] [-vmargs JavaVMarguments]</code>
+ * where
+ * <ul>
+ * <li><code>eclipseInstallPath</code> specifies Eclipse installation directory;
+ * it must be provided, when current directory is not the same
+ * as Eclipse installation directory,</li>
+ * <li><code>helpServerHost</code> specifies host name of the interface
+ * that help server will use, it overrides host name specified
+ * the application server plugin preferences</li>
+ * <li><code>helpServerPort</code> specifies port number
+ * that help server will use, it overrides port number specified
+ * the application server plugin preferences.</li>
+ * <li><code>timeout</code> number of seconds waiting
+ * to connect to the help server while executing commands,
+ * such as shutdown. Default is 40s. You may need to
+ * use this option when running from a slow media such as a CD-ROM.
+ * <li><code>-noexec</code> option indicates that
+ * Eclipse executable should not be used.</li>
+ * <li><code>platform options</code> are other options that are supported by Eclipse Executable.</li>
+ * <ul>
+ */
+ public static void main(String[] args) {
+ StandaloneInfocenter.main(args);
+ }
+}
diff --git a/org.eclipse.help.base/src/org/eclipse/help/standalone/package.html b/org.eclipse.help.base/src/org/eclipse/help/standalone/package.html
new file mode 100644
index 000000000..ded158b98
--- /dev/null
+++ b/org.eclipse.help.base/src/org/eclipse/help/standalone/package.html
@@ -0,0 +1,27 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides access to infocenter and stand alone help system.&nbsp;
+Classes in this package are not supposed to be called by Eclipse plugins,
+they are intended to be called from outside of Eclipse environment.
+<h2>
+Package Specification</h2>The help system in Eclipse can be used to run an information center or as a stand alone help system for use by other products.&nbsp; Access
+to the help system is provided
+<TT>org.eclipse.help.standalone.Infocenter </TT> and
+<TT>org.eclipse.help.standalone.Help </TT>
+classes.
+<P>To start or stop the infocenter, use the Infocenter class. The class can be used as a stand alone program, launched from a command line, or can be instantiated and used from a Java program.</P>
+<p>To start, stop the stand alone help system or cause it to display help on a user machine, use the Help class. The class can be used as a stand alone program, launched from a command line, or can be instantiated and used from a Java program.</P>
+<P>There
+is a general mechanism (defined at the plug-in level) by which individual
+plug-ins contribute on-line help and context-sensitive help for their component.
+This Eclipse mechanism for contributing help content needs to be used.&nbsp;
+The stand alone help system is responsible for accessing this information
+and displaying it to the user. The infocenter, unlike the stand alone help system, does not launch UI on the machine that it is running on. When infocenter is started the help content can be obtained by connecting to a help server (port specified in Tomcat plugin preferences) using HTTP connection.</P></body>
+</html>

Back to the top