HowToUseSearchAPI

Warning: This page is work in progress, the API has not been added to NetBeans platform, and maybe it will never be.

Contents

Overview

You will need to use this API:

  • if you implement a custom project type, and want to define which files and directories should be searched and which should be skipped.
  • if you want to provide customized search types that require special settings and algorithms (e.g. searching in a specific file type, searching in database), and want them to be accessible from standard search panel (Ctrl-Shift-F) or replace panel (Ctrl-Shift-H).


Customizing search in project

Here we will learn how to set which files will be searched and which skipped.

The more complex way - SearchInfoDefinition

TODO

Simpler way - SubTreeSearchOptions

TODO


Provide custom search

This part of Search API is useful to add specialized search to platform applications, or to enhance the IDE with optimized searching (for various operating systems, search options, etc.).

What happens when you register your custom search provider? The Find in Projects dialog will contain not single form with search options, but tabbed panel where each tab will contain a form for its provider.

TODO: Add Screenshot - search dialog with tabs

You will need to implement several things:

  • SearchProvider - to register your search, tell what it can do and whether it is enabled.
  • SearchProvider.Presenter - to create a visual component for displaying settings and to let it interact with search dialog.
  • SearchComposition - to start and control search algorithm.
  • SearchResultsDisplayer - an component that can display matching objects and some controls in the Search Results window (you can used simple, predefined implementation, if you are comfortable with it).

Example: Simple provider, searching for recently modified files.

/**
 * Provider for searching recently modified files.
 */
@ServiceProvider(service = SearchProvider.class)
public class CustomProvider extends SearchProvider {

    @Override
    public Presenter createPresenter(boolean replaceMode) {
        return new CustomPresenter(this);
    }

    @Override
    public boolean isReplaceSupported() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public String getTitle() {
        return "Recent Files Search";
    }

    /**
     * Presenter is an object that holds a UI component for setting search
     * criteria.
     */
    private class CustomPresenter extends Presenter {

        private JPanel panel = null;
        ScopeOptionsController scopeSettingsPanel;
        FileNameController fileNameComboBox;
        ScopeController scopeComboBox;
        ChangeListener changeListener;
        JSlider slider;

        public CustomPresenter(SearchProvider searchProvider) {
            super(searchProvider, false);
        }

        /**
         * Get UI component that can be added to the search dialog.
         */
        @Override
        public synchronized JComponent getForm() {
            if (panel == null) {
                panel = new JPanel();
                panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));

                JPanel row1 = new JPanel(new FlowLayout(FlowLayout.LEADING));
                JPanel row2 = new JPanel(new FlowLayout(FlowLayout.LEADING));
                JPanel row3 = new JPanel(new FlowLayout(FlowLayout.LEADING));
                row1.add(new JLabel("Age in hours: "));
                slider = new JSlider(1, 72);
                row1.add(slider);
                final JLabel hoursLabel = new JLabel(String.valueOf(slider.getValue()));
                row1.add(hoursLabel);

                row2.add(new JLabel("File name: "));
                fileNameComboBox = ComponentUtils.adjustComboForFileName(new JComboBox());
                row2.add(fileNameComboBox.getComponent());

                scopeSettingsPanel = ComponentUtils.adjustPanelForOptions(new JPanel(),
                        false, fileNameComboBox);

                row3.add(new JLabel("Scope: "));
                scopeComboBox = ComponentUtils.adjustComboForScope(new JComboBox(), null);
                row3.add(scopeComboBox.getComponent());
                panel.add(row1);
                panel.add(row3);
                panel.add(row2);
                panel.add(scopeSettingsPanel.getComponent());
                initChangeListener();

                slider.addChangeListener(new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        hoursLabel.setText(String.valueOf(slider.getValue()));
                    }
                });
            }
            return panel;
        }

        private void initChangeListener() {
            this.changeListener = new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    fireChange();
                }
            };
            fileNameComboBox.addChangeListener(changeListener);
            scopeSettingsPanel.addChangeListener(changeListener);
            slider.addChangeListener(changeListener);
        }

        @Override
        public HelpCtx getHelpCtx() {
            return new HelpCtx("org.netbeans.modules.search.ui.prototype.about");  //NOI18N
        }

        /**
         * Create search composition for criteria specified in the form.
         */
        @Override
        public SearchComposition<?> composeSearch() {
            SearchScopeOptions sso = scopeSettingsPanel.getSearchScopeOptions();
            return new CustomComposition(sso, scopeComboBox.getSearchInfo(),
                    slider.getValue(), this);
        }

        /**
         * Here we return always true, but could return false e.g. if file name
         * pattern is empty.
         */
        @Override
        public boolean isUsable(NotificationLineSupport notifySupport) {
            return true;
        }
    }

    /**
     * Custom algorithm that check date of last modification of searched files.
     */
    private class CustomComposition extends SearchComposition<DataObject> {

        SearchScopeOptions searchScopeOptions;
        SearchInfo searchInfo;
        int oldInHours;
        SearchResultsDisplayer<DataObject> resultsDisplayer;
        private final Presenter presenter;
        AtomicBoolean terminated = new AtomicBoolean(false);

        public CustomComposition(SearchScopeOptions searchScopeOptions,
                SearchInfo searchInfo, int oldInHours, Presenter presenter) {

            this.searchScopeOptions = searchScopeOptions;
            this.searchInfo = searchInfo;
            this.oldInHours = oldInHours;
            this.presenter = presenter;
        }

        @Override
        public void start(SearchListener listener) {

            for (FileObject fo : searchInfo.getFilesToSearch(
                    searchScopeOptions, listener, terminated)) {
                if (ageInHours(fo) < oldInHours) {
                    try {
                        DataObject dob = DataObject.find(fo);
                        getSearchResultsDisplayer().addMatchingObject(dob);
                    } catch (DataObjectNotFoundException ex) {
                        listener.fileContentMatchingError(fo.getPath(), ex);
                    }
                }
            }
        }

        @Override
        public void terminate() {
            terminated.set(true);
        }

        @Override
        public boolean isTerminated() {
            return terminated.get();
        }

        /**
         * Use default displayer to show search results.
         */
        @Override
        public synchronized SearchResultsDisplayer<DataObject> getSearchResultsDisplayer() {
            if (resultsDisplayer == null) {
                resultsDisplayer = createResultsDisplayer();
            }
            return resultsDisplayer;
        }

        private SearchResultsDisplayer<DataObject> createResultsDisplayer() {

            /**
             * Object to transform matching objects to nodes.
             */
            SearchResultsDisplayer.NodeDisplayer<DataObject> nd =
                    new SearchResultsDisplayer.NodeDisplayer<DataObject>() {
                        @Override
                        public org.openide.nodes.Node matchToNode(
                                final DataObject match) {
                            return new FilterNode(match.getNodeDelegate()) {
                                @Override
                                public String getDisplayName() {
                                    return super.getDisplayName()
                                            + " (" + ageInMinutes(match.getPrimaryFile()) + " minutes old)";
                                }
                            };
                        }
                    };
            return SearchResultsDisplayer.createDefault(nd, this,
                    presenter, "less than " + oldInHours + " hours old");
        }
    }

    private static long ageInMinutes(FileObject fo) {
        long fileDate = fo.lastModified().getTime();
        long now = System.currentTimeMillis();
        return (now - fileDate) / 60000;
    }

    private static long ageInHours(FileObject fo) {
        return ageInMinutes(fo) / 60;
    }
}

TODO: Add examples - usage of SearchControl

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo