Column View Search
Author: Nathan FiedlerThe XML Schema editor supports three views, one showing the raw text, another displays interactive graphs of the component relationships, and a third shows the components in a tree/table hybrid (referred to as the column view). The text editor has a suitable search facility which is supplied by the NetBeans IDE. This specification describes the search feature as it exists in the columns view.
Below is a screen shot of the columns view. Note that the search toolbar is not visible until the Find action is invoked, either from the Edit menu, the IDE toolbar, or by pressing the Ctrl-F keyboard shortcut. Note that the Schema view has both a columns mode and a tree mode (the two small icons in the view toolbar). The search feature works identically in both views.
Search Field
To start with, we will take a brief look at the search interface of a popular desktop application used by many Sun employees, Mozilla Thunderbird. The search field in Mozilla Thunderbird is always visible in the area just below the toolbar. The field is not labelled, but an icon just inside the field depicts a magnifying glass, which is easily interpreted to represent searching.
Normally, it is quite common for a magnifying glass to represent a zoom feature, in which a graphical view is magnified to increase the size of the elements within the view. In some software products, the search feature is represented by binoculars (e.g. Adobe Reader). Given that the two most popular web browsers (IE and Firefox) both represent their search feature with a magnifying glass, it seems reasonable to use a similar icon for our search feature.
Thunderbird offers the ability to search over one or more fields of a mail message, although the search phrase is always treated as a simple text string. The interface for choosing which field or fields to search through is offered by a menu accessed from a button in the search field, as shown below.
The column view search should offer a similar feature, in which a query phrase could be matched against one of several types of components in an XML Schema document. For instance:
- Component Name
- Finds components whose name attribute matches the search phrase.
- Component Kind
- Finds components whose kind matches the search query.
- Attribute Declaration
- Finds components with attributes of a particular name.
- Attribute Value
- Finds components with attributes of a particular value.
- Namespace Usage
- Finds named component references that reference components of a particular namespace.
XPathFinds components that match the given XPath.(This option is not provided at present due to it being very slow.)
Search Query
There are several types of query strings, depending on what the user provides. By default the query phrase is treated as a simple sub-string, unless it contains wildcards (*, ?), in which case it is a wildcard search. If the search type is XPath, then the query must be a valid XPath expression. Likewise, if the "Use regular expression" menu item is checked, then the query phrase must be a valid Perl-like regular expression. Each type of query phrase is described below.
- Simple sub-string
- The query phrase is treated as a case-insensitive
sub-string. That is, the phrase is considered a
match if it is equal to the target text, whether in whole or in
part. For example,
leng
with match "minLength", "maxlength", and even "LENG". - Basic wildcard expression
- The query phrase is treated as a case-insensitive, simple
wildcard expression. The expression must match the target text in
its entirety to constitute a match. The allowed wildcards are
asterisk (*) for zero or more characters, and question mark (?) for
exactly one character. For example,
floss*
will match any text that begins with "floss", including just "floss", whileloss?
will match "lossy" but not "loss". Using*
will match anything, while???
only matches text comprised of three characters. Wildcards can be combined, and appear more than once in the expression, sofloss*pas??
would match "floss and paste". - Perl-like regular expression
- See the
java.util.regex.Pattern
class API documentation for the details on the regular expression format. - XPath expression
- An XPath expression, with case-sensitivity. The expression must match the component path in its entirety in order to constitute a match (i.e. no sub-string matching). See the XML Path Language specification for more information.
Note that search queries that are not sub-string expressions will
require that the expression contains any necessary namespace prefix
in order to match components. That is, if your schema contains
components with a prefix such as xs:
, then your query
should either include a wildcard prefix (e.g. *
), or the
namespace prefix. For instance, searching for min* may
find nothing at all, while either xs:min* or
*min* will likely find what you are expecting. This
applies to the "Basic wildcard expression", "Perl-like
regular expression", and "XPath expression" query
types.
Search Options
The search menu provides options for controlling how the search is performed, and they are as follows.
- Search from selected
- Starts the search from the component most recently selected in the columns view interface. That is, rather than searching from the root of the schema document, the search is performed with the selected sub-tree. Note that this option is not supported by the XPath query search.
- Use regular expression
- When selected, the phrase entered into the text field is treated as a Perl-like regular expression, with case-sensitivity, as described above. Note that this option is not supported by the XPath query search.
Displaying Search Results
The column view displays the XML schema in a table form in which there is only one row, with multiple columns. Displaying the results of a search in this view would likely take one of two forms.
The first would be to hide those components that do not match the query phrase, or do not lead to a matching component. For example, Mozilla Thunderbird hides those messages which do not match the given search phrase, as shown in the image below.
In our column view image shown above, if the search query was for \d{5} then the only components visible might be Address, zip, and \d{5} itself. That is, only the components which match, and the components leading up to those matches, would be shown in the columns view. The image below provides an example of this. Note that the query example here is not a regular expression.
The second form of indicating the matching components would be to highlight those components that match the query, or that lead to a matching component. This is similar to the search facility in Mozilla Firefox, in which the text matching a query phrase is highlighted with a bright color. Since changing the background color would conflict with the painting of the selection highlight, the search will change the foreground color and render the text in bold.
Components which match the query are highlighted with a dark orange color, while the components that lead to a matching component are colored with a light orange. This is to help the user distinguish between the actual results and those components that simply lead to a result. The first matching result will be selected in the view, possibly opening columns (or expanding the tree if the tree view is selected).
If the search phrase appears in multiple locations, then entries within each visible column will be highlighted appropriately. In this way, the user can quickly navigate from the root components down to the matching component. In addition to the highlight, the search toolbar provides a buttons for moving to the next and previous search results. These buttons will wrap around, such that if the user keeps pressing one button or the other, they will first see all of the results, and then start from the beginning again.
The preferred form for indicating the search results is to highlight the matching components. The reason for this is that the context of the matching components may be lost if all other components are hidden. In particular, Chris Webster pointed out that components of a sequence may no longer make sense when viewed alone.
Indicating No Match
Mozilla Firefox indicates that the query phrase could not be found by highlighting the search field in red, emitting a beep sound, and adding a Phrase not found message in the search toolbar. It seems very unlikely that our search facility should ever beep, as it would probably be the first time the IDE ever made a noise. Secondly, while the Phrase not found label is certainly clear and concise, it would not fit in our current toolbar area. This approach would seem to work best when the search results are highlighted rather than filtered.
An alternative to the red highlight in the search field would be to hide all of the components in the column view. This is what Thunderbird does when the query phrase does not match any messages. Naturally this makes the most sense when paired with the filtering of search results. That is, if the search results are highlighted, then no components will be filtered, and instead the search field will be highlighted in red. Since our search results will be highlighted, the search field will be highlighted when there is no match, as shown in the screen shot below.
Dismissing the Search
To cancel the active search and restore the column view to its normal display of all of the components in the schema, the IDE offers a red 'x' icon in the search toolbar. This is what Firefox does and it is sufficiently intuitable such that an explanation is not really necessary. In the case of highlighting the search results, the close button removes the highlight effect from the view.
Implementation Details
The search implementation(s) generally subclass
DeepSchemaVisitor
and add each matching component to a
Set
. The path from the matching component to the schema
root is determined by using getParent()
to walk up the
tree. In this way, it is possible to visit every component in the
schema model and store the complete path of each matching component.
With the complete path of each matching component, it is trivial to
highlight the search results in the columns view.
The search interface is implemented in the
SchemaSearchPanel
and SearchFieldPanel
classes, both in the XML Schema UI - Basic module.
The logic of dealing with initiating the search and handling user
input is in SearchFieldPanel
, while
SchemaSearchPanel
manages the search toolbar and the
close, next, and previous buttons.
Highlighting the search results is accomplished by creating
Highlight
objects and registering them with the default
HighlightManager
. Each Highlight
instance
is associated with a single SchemaComponentReference
,
and the SchemaComponentNode
and
CategoryNode
classes implement the
Highlighted
interface in order to render the search
results appropriately.
The search field displays the type of the search that would be performed if a phrase were provided (e.g. Component Name) using the inactive text color in the text field. When the field gains the input focus, the text will disappear and a text cursor will indicate that it is ready to receive the search phrase. When focus is lost and the field is blank, the search type string will re-appear. The search type string also disappears when the search type menu is opened, and re-appear when the menu closes.
Each of the view Category
implementations provide
zero or more SearchProvider
implementations in their
Lookup
. If no search providers are available in the
currently active category, then the search field in the toolbar is
disabled to indicate the search feature is unavailable. The
SearchProvider
interface includes the following
methods:
String getDisplayName()
- Returns the display string for describing this search provider.
String getInputDescription()
- Returns a description of the expected input for this provider. This is used to instruct the user on what should be entered into the search field.
String getShortDescription()
- Returns a brief description of this search provider, useful for tooltips.
List<SchemaComponent> search(Query query)
- Performs the search using the given query string, and returns the list of matches that were found.
Note that Query
is a simple class that encapsulates
the search phrase and the search parameters.
The search interface code listens to the category selection (via
property changes in the CategoryPane
) and change the
enabled state of the search field accordingly (as described above).
If the current category provides one or more search providers, then
the "search types" described earlier will correspond to the
available search providers.