// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= 快速搜索集成教程
:jbake-type: platform_tutorial
:jbake-tags: tutorials 
:jbake-status: published
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:icons: font
:experimental:
:description: 快速搜索集成教程 - Apache NetBeans
:keywords: Apache NetBeans Platform, Platform Tutorials, 快速搜索集成教程

本教程介绍如何编写在 NetBeans 快速搜索功能中集成新项目的模块。








（可选）要解决疑难问题，可以 link:http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=11179[ 下载完整的样例 ]并查看其源代码。


== [[快速搜索集成简介]] 

NetBeans IDE 6.5 中引入的快速搜索功能由 IDE 右上角的一个文本字段组成。在该字段中键入搜索字符串时，会出现一个下拉列表并显示匹配的项目。缺省情况下，这些项目来自在 IDE 中注册的操作名称以及 IDE 的 Java 帮助中的帮助主题。选择操作项目将调用该操作；选择帮助项目将在 JavaHelp 中打开该主题。

此外， link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/overview-summary.html[快速搜索 API] 已被公开。您可以使用它将您自己的搜索项目集成到快速搜索功能中。您可以在 IDE 中使用该功能，也可以在 NetBeans 平台上将它作为应用程序的一部分使用。

在本教程，您将创建一个模块，用于将  link:http://netbeans.dzone.com[NetBeans Zone] 中的项目集成到快速搜索功能中：


image::images/qsearch_deployed-result.png[]


== 创建模块项目

在此部分，我们将利用向导创建每个 NetBeans 模块所需的源代码结构。源代码结构包含位于特定位置的某些文件夹以及一组始终需要的文件。例如，每个 NetBeans 模块都需要一个  ``nbproject``  文件夹和一个  ``layer.xml``  文件，前者用于存放项目的 meta 数据，后者用于工具栏按钮和窗口等项的声明性注册。


[start=1]
1. 选择“文件”>“新建项目”(Ctrl-Shift-N)。在“类别”下选择“NetBeans 模块”。在“项目”下面，选择“模块”并单击“下一步”。

[start=2]
1. 在“名称和位置”面板的“项目名称”中键入  ``NetBeansZoneSearch`` 。将项目位置更改为计算机上的任意目录，例如  ``c:\mymodules`` 。将“独立模块”单选按钮保留为选中状态。此面板现在应如下所示：


image::images/qsearch_new-module-1.png[]

单击“下一步”。


[start=3]
1. 在“基本模块配置”面板中，键入  ``org.netbeans.modules.nbzone``  作为代码名称基。在建议的模块显示名称中添加空格，以将其更改为  ``NetBeans Zone Search`` 。选中“生成 XML 层”复选框，并保留本地化绑定和 XML 层的位置不变，这样它们将存储在名称为  ``org/netbeans/modules/demo``  的包中。此面板现在应如下所示：


image::images/qsearch_new-module-2.png[]


[start=4]
1. 单击“完成”。

IDE 将创建  ``NetBeans Zone Search``  项目。此项目包含所有源代码和项目 meta 数据，例如项目的 Ant 生成脚本。此项目在 IDE 中打开。您可以在“项目”窗口 (Ctrl-1) 中查看其逻辑结构，在“文件”窗口 (Ctrl-2) 中查看其文件结构。 


== [[使用“快速搜索提供器”向导]] 

在此部分，我们将通过向导来创建实现快速搜索功能集成所需的桩模块类和层条目。


[start=1]
1. 右键单击项目节点，然后选择“新建”>“其他”。在“新建文件”对话框中，选择“模块开发”>“快速搜索提供器”。

[start=2]
1. 在“快速搜索提供器”面板中，设置以下信息：

* *提供器类名。*指定向导将生成的桩模块的类名。在该字段中键入 "NBZoneSearchProvider"。
* *包。*指定将在其中生成桩模块类的包。从下拉列表中选择 "org.netbeans.modules.nbzone"。
* *类别显示名称。*指定桩模块将创建的类别的显示名称。在该字段中键入 "NetBeans Zone"。
* *命令前缀。*指定在搜索桩模块将创建的类别时，用于缩小搜索范围的前缀。在该字段中键入 "nb"。
* *弹出式窗口中的位置。*指定新类别在快速搜索功能中的位置。保留值为 "0" 不变，这样类别将处于弹出式窗口第一个位置。

您现在应该看到下面的屏幕：


image::images/qsearch_quick-search-template.png[]


[start=3]
1. 单击“完成”。

“项目”窗口现在应如下所示：


image::images/qsearch_projects-window-final.png[]

在  ``layer.xml``  文件中，您应该能看到以下内容：


[source,xml]
----

<filesystem>
    <folder name="QuickSearch">
        <folder name="NetBeansZone">
            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.nbzone.Bundle"/>
            <attr name="command" stringvalue="nb"/>
            <attr name="position" intvalue="0"/>
            <file name="org-netbeans-modules-nbzone-NBZoneSearchProvider.instance"/>
        </folder>
    </folder>
</filesystem>
----



== 集成外部 HTML DOM 解析器

在下一部分中，我们需要一个 HTML DOM 解析器来解析 NetBeans Zone。您可以选择任何合适的解析器。考虑到本教程的目的，我们将使用  link:http://sourceforge.net/project/showfiles.php?group_id=13153[JTidy]。

可以采用两种方法将外部 JAR 文件集成到模块中。第一种方法是将 JAR 放置在一个单独的“库包装器模块” 中，并在将两个模块集成到模块套件中之前让功能模块_依赖于_该库包装器模块。使用两个单独模块的好处在于，当新版本的外部 JAR 发行时，您只需要重新分发一个较小的模块（其中只包含外部 JAR），而不是还包含功能代码的大模块。第二种方法是将 JAR 添加到功能模块中，下面采用的就是这种方法。这种方法的好处在于，它在短期内比较便利，因为您只需要分发一个模块，而它的缺点在于，您会将外部库和功能代码混合在一起，而这不是一种严格的模块化方法。


[start=1]
1. 下载  link:http://sourceforge.net/project/showfiles.php?group_id=13153[JTidy] 并找到下载包中的  ``Tidy.jar`` 。

[start=2]
1. 在“文件”窗口中，创建如下所示的文件夹结构，将  ``Tidy.jar``  放置在  ``release/modules/ext``  文件夹中：


image::images/qsearch_tidyjar.png[]


[start=3]
1. 在  ``project.xml``  文件的结束部分，该文件位于  ``nbproject``  文件夹，添加下面的粗体标记：

[source,xml]
----


            ...
            ...
            ...
            *<class-path-extension>
                <runtime-relative-path>ext/Tidy.jar</runtime-relative-path>
                <binary-origin>release/modules/ext/Tidy.jar</binary-origin>
            </class-path-extension>*
        </data>
    </configuration>
 </project>
----


[start=4]
1. 在  ``project.properties``  文件中，添加以下内容：

[source,java]
----

cp.extra=release/modules/ext/Tidy.jar
----

现在，外部 HTML DOM 解析器已经在您模块的类路径中。您可以使用 JAR 中的类，如下一部分所示。


== 实现快速搜索集成

接下来，我们将实现 API。API 的类如下所示：

|===
|类 |描述 

| link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchProvider.html[SearchProvider] |快速搜索 API 的主接口。实现此接口，为您的快速搜索提供新的结果分组。 

| link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchRequest.html[SearchRequest] |快速搜索请求的描述。 

| link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchResponse.html[SearchResponse] |收集 SearchRequest 结果的响应对象。 
|===

下面，我们将设置所需模块的依赖关系，然后在我们自己的模块中实现它们。


[start=1]
1. 右键单击项目，选择“属性”，在“库”面板中设置以下 个依赖关系。


image::images/qsearch_set-dependencies.png[]


[start=2]
1. 打开生成的类。

[start=3]
1. 修改该类，如下所示：

[source,java]
----

public class NBZoneSearchProvider implements  link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchProvider.html[SearchProvider] {

    /**
     * Method is called by infrastructure when search operation is requested.* Implementors should evaluate given request and fill response object with
     * apropriate results
     *
     * @param request Search request object that contains search string
     * @param response Search response object that stores search results 
     * Note that it's important to react to return value of 
     * SearchResponse.addResult(...) method and stop computation if 
     * false value is returned.
     */
    @Override 
    public void evaluate( link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchRequest.html[SearchRequest request],  link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchResponse.html[SearchResponse response]) {
        try {

            *//The URL that we are providing a search for:*
            URL url = new URL("http://netbeans.dzone.com");
            *//Stuff needed by Tidy:*
            Tidy tidy = new Tidy();
            tidy.setXHTML(true);
            tidy.setTidyMark(false);
            tidy.setShowWarnings(false);
            tidy.setQuiet(true);

            *//Get the org.w3c.dom.Document from Tidy,
            //or use a different parser of your choice:*
            Document doc = tidy.parseDOM(url.openStream(), null);

            *//Get all "a" elements:*
            NodeList list = doc.getElementsByTagName("a");

            *//Get the number of elements:*
            int length = list.getLength();

            *//Loop through all the "a" elements:*
            for (int i = 0; i < length; i++) {

                String href = null;
                if (null != list.item(i).getAttributes().getNamedItem("href")) {
                    *//Get the "href" attribute from the current "a" element:*
                    href = list.item(i).getAttributes().getNamedItem("href").getNodeValue();
                }

                *//Get the "title" attribute from the current "a" element:*
                if (null != list.item(i).getAttributes().getNamedItem("title")) {
                    String title = list.item(i).getAttributes().getNamedItem("title").getNodeValue();

                    *//If the title matches the requested text:*
                    if (title.toLowerCase().indexOf( link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchRequest.html[request.getText().toLowerCase()]) != -1) {

                        *//Add the runnable and the title to the response
                        //and return if nothing is added:*
                        if (! link:http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-quicksearch/org/netbeans/spi/quicksearch/SearchResponse.html[response.addResult(new OpenFoundArticle(href), title)]) {
                            return;
                        }

                    }

                }

            }

        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    private static class OpenFoundArticle implements Runnable {

        private String article;

        public OpenFoundArticle(String article) {
            this.article = article;
        }

        public void run() {
            try {
                URL url = new URL("http://netbeans.dzone.com" + article);
                StatusDisplayer.getDefault().setStatusText(url.toString());
                URLDisplayer.getDefault().showURL(url);
            } catch (MalformedURLException ex) {
                Logger.getLogger(NBZoneSearchProvider.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
    } 
    
 }
----


[start=4]
1. 确保声明了以下导入数据：

[source,java]
----

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.spi.quicksearch.SearchProvider;
import org.netbeans.spi.quicksearch.SearchRequest;
import org.netbeans.spi.quicksearch.SearchResponse;
import org.openide.awt.HtmlBrowser.URLDisplayer;
import org.openide.awt.StatusDisplayer;
import org.openide.util.Exceptions;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
----


== 安装并试用功能

现在，安装模块并使用快速搜索功能集成。IDE 使用 Ant 生成脚本来生成和安装模块。此生成脚本是在创建项目时创建的。


[start=1]
1. 在“项目”窗口中，右键单击项目并选择“运行”。

此时将启动一个新的 IDE 实例，并安装快速搜索集成模块。


[start=2]
1. 在快速搜索功能中键入一个字符串，如果字符串匹配 NetBeans Zone 中的某个标题，则 NetBeans Zone 中的该项目将包括在结果中：


image::images/qsearch_deployed-result.png[]

如果您键入在  ``layer.xml``  中定义的命令前缀，并紧跟一个空格，则只搜索相关的类别：


image::images/qsearch_command.png[]


[start=3]
1. 单击某个项目，如果您在 IDE 中设置了浏览器，则会打开该浏览器并显示所选的文章。



== [[在 NetBeans 平台上使用快速搜索功能]] 

上一部分假定您为现有应用程序创建了一个模块。如果您要在 NetBeans 平台上创建自己的应用程序，而不是创建模块，请阅读下面两个主题。


=== 在 NetBeans 平台上启用快速搜索功能

虽然 NetBeans IDE 随带了对快速搜索功能的支持，但 NetBeans 平台却并非如此。缺省情况下，快速搜索功能是隐藏的。根据下面的步骤启用该功能。


[start=1]
1. 将以下标记添加到  ``layer.xml``  文件中：

[source,xml]
----

<folder name="Toolbars">
    <folder name="QuickSearch">
        <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.nbzone.Bundle"/>
        <file name="org-netbeans-modules-quicksearch-QuickSearchAction.shadow">
            <attr name="originalFile" 
            stringvalue="Actions/Edit/org-netbeans-modules-quicksearch-QuickSearchAction.instance"/>
        </file>
    </folder>
</folder>
----


[start=2]
1. 将此键/值对添加到  ``Bundle.properties``  文件 中：

[source,java]
----

Toolbars/QuickSearch=Quick Search
----


[start=3]
1. 运行 NetBeans 平台应用程序，您应该能看到快速搜索功能现已可用：


image::images/qsearch_netbeans-platform-qsearch.png[]


=== 在 NetBeans 平台上启用缺省 Web 搜索提供器

缺省 Web 搜索提供器实现可以用在 NetBeans 源代码中。该提供器在 Google 中搜索与搜索字符串匹配的文本。在 IDE 中，它的作用是在  ``netbeans.org``  和相关站点中搜索与 IDE 相关的在线文档。

*注意：*遗憾的是，IDE 中禁用了 Web 搜索提供器，因为在多次使用之后，Google 会抱怨自动搜索违背了其使用条款，并拒绝继续提供服务。

如果您接受上述限制，您可以标记此 Web 搜索提供器并在您的 NetBeans 平台应用程序中用它。


[start=1]
1. 如前所述，确保已经启用快速搜索功能。

[start=2]
1. 将以下标记添加到  ``layer.xml``  文件中：

[source,xml]
----

<folder name="Guardian">
   <file name="org-netbeans-modules-quicksearch-web-WebQuickSearchProviderImpl.instance"/>
</folder>
----


[start=3]
1. 在应用程序的 " ``branding`` " 文件夹中，创建如下所示的文件夹分层结构以及  ``Bundle.properties``  文件：


image::images/qsearch_brand-provider.png[]

在 IDE 中，上述属性是硬编码在代码中的，但是对于 NetBeans 平台，它们没有自己的定义，因此需要像上面那样标记这些属性：


[source,java]
----

quicksearch.web.site=netbeans.org
quicksearch.web.url_patterns=.*netbeans\.org/kb.*,\
    /.*wiki\.netbeans\.org/.*faq.*,.*wiki\.netbeans\.org/.*howto.*,\
    .*platform\.netbeans\.org/tutorials.*
----


[start=4]
1. 运行 NetBeans 平台应用程序，您应该能看到 Web 快速搜索功能现已可用：


image::images/qsearch_clare-wigfall.png[]


== 创建可共享的模块二进制文件

该模块现已完成，您可以将其交给其他用户使用了。为此，您需要创建一个 "NBM"（NetBeans 模块）二进制文件并分发它。


[start=1]
1. 在“项目”窗口中，右键单击“ ``NetBeans Zone 搜索`` ”项目，然后选择“创建 NBM”。

此时将创建 NBM 文件，您可以在“文件”窗口 (Ctrl-2) 中查看它：


image::images/qsearch_shareable-binary.png[]


[start=2]
1. 例如，通过  link:http://plugins.netbeans.org/PluginPortal/[NetBeans 插件门户]向其他人提供该文件。接收者应使用插件管理器（“工具”>“插件”）来安装它。


link:http://netbeans.apache.org/community/mailing-lists.html[请将您的意见和建议发送给我们]



== 后续步骤

有关创建和开发 NetBeans 模块的详细信息，请参见以下资源：

*  link:https://netbeans.apache.org/platform/index.html[NetBeans 平台主页]
*  link:http://bits.netbeans.org/dev/javadoc/index.html[NetBeans API 列表（当前开发版本）]
*  link:https://netbeans.apache.org/kb/docs/platform_zh_CN.html[其他相关教程]

