Basics of working with structural search

From IntelliJ-Wiki

Jump to: navigation, search

This tutorial offers a set of exercises intended to help you get started with structural search in IntelliJ IDEA.

First, you will:

Then, you will perform a number of search exercises and learn how to:

Contents

Additional sources of information

You can read more about the structural search in:

Before you start

To be able to perform the exercises discussed in this tutorial, make sure that the following software is installed on your computer:

  • IntelliJ IDEA Ultimate Edition, version 12.
  • Java Development Kit (JDK), version 1.6 or later. You can download the necessary version of Oracle JDK from the Java SE Downloads page.

Understanding structural search concepts

Structural search is a search for language constructs in your source code (Java, html, xml, etc.).

You specify what you want to find using search templates. Templates are valid language constructs containing fixed text and variables.

A typical example of the search template is class $Class$ {}. In this template, class and {} is the fixed part. $Class$ is a variable which, in this template, represents the name of the class. (Variables in search templates are text fragments enclosed between the dollar sings $...$.)

To make your search more precise, you can define various types of constraints for any variable within a template. The most popular types of constraints are text constraints and occurrence count constraints. The text constraints define the (text) patterns that the value of a variable must match. The occurrence count constraints specify how many times similar syntactic units represented by a variable may occur in the source code.

There may be one or more search targets associated with a template. The search target is either the whole template, or one or more variables within the template. The targets represent the part that, within the template, is of most interest to you.

As a rule, code formatting in search templates and source code does not matter. Sometimes, when matching a template against the source code, certain semantic aspects are also taken into account.

Preparing for the search

To be able to perform structural search, the Structural Search plugin must be enabled. Besides, you need something to search within, that is, a project with at least one Java class. So, to prepare for the search:

1. Make sure that the Structural Search plugin is enabled.

2. Create a project with a Java module.

3. Create a Java class in the the src directory.

4. Copy the source code that we have prepared into the class.

Once you've done that, you are ready to start the structural search exercises.

Making sure that the Structural Search plugin is enabled

The structural search and replace functionality in IntelliJ IDEA is based on the Structural Search plugin. So, the first thing to do is to make sure that this plugin is enabled:

1. Open the Settings dialog (e.g. by pressing CTRL+ALT+S).

2. In the left-hand pane of the dialog, under IDE Settings, select Plugins (1 on the following picture).

3. On the Plugins page that opens in the right-hand part of the dialog, type str in the search box (2). As a result, only the plugins whose names and descriptions contain str are shown in the list.

Make sure that the check box next to Structural Search is selected (3).

File:I12SSR01SettingsPlugins.png

4. Click OK (4).

5. If suggested, restart IntelliJ IDEA.

Creating a project

To create a project with a Java module:

1. If no project is currently open in IntelliJ IDEA, click Create New Project on the Welcome screen. Otherwise, select New Project from the File menu.

As a result, the New Project wizard opens.

2. In the left-hand pane, select Java Module (1).

3. In the right-hand part of the page, in the Project name field, type the name of your new project (e.g. SSRDemo) (2).

File:I12SSR02NewProjectName.png

4. If the JDK that you are going to use is already defined in IntelliJ IDEA, select this JDK from the Project SDK list, click Next and proceed to the next step.

Otherwise, define the necessary JDK in the IDE and associate it with the project that you are creating. To do that:

Click New to the right of the Project SDK field (1) and select JDK (2).

File:I12SSR03NewProjectNewJDK.png

In the Select Home Directory for JDK dialog that opens, select the directory in which you have the desired JDK installed (1), and click OK (2).

File:I12SSR04JDKHome.png

The JDK you have just specified is shown in the Project SDK field.

File:I12SSR05NewProjectJDKSpecified.png

Click Next.

5. On the next page of the wizard (where you are asked to select the technologies to be supported), just click Finish to complete the wizard.

Wait while IntelliJ IDEA is creating the necessary project structures. When this process is complete, you can see the structure of your new project in the Project tool window.

Creating a new Java class

To create a new Java class:

1. In the Project tool window, right-click the src folder (1), point to New (2), and then click Java Class (3).

File:I12SSR06SrcNewJavaClass.png

2. In the Create New Class dialog, type the name of the class in the Name field (for example, MetersToInchesConverter; this name reflects the functionality that the class will implement) (1) and click OK (2).

File:I12SSR07NewClassName.png

As a result, the class with the specified name is created and opened in the editor.

File:I12SSR08ClassCreated.png

Copying the source code

1. In MetersToInchesConverter.java, replace all the file contents with the following:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class MetersToInchesConverter {

    static boolean processRepeatConversion (String userInput) {
        boolean repeatConversion = false;
        if (((userInput.equals("y")) || (userInput.equals("Y")))) {
            repeatConversion = true;
        }
        return repeatConversion;
    }

    static String[] convertMetersToInches (String metersToConvertString) {
        String[] conversionResult = new String[2];
        double conversionFactor = 39.37;
        try {
            double metersToConvertDouble = Double.parseDouble(metersToConvertString);
            double inchesDouble = metersToConvertDouble*conversionFactor;
            String inchesString = Double.toString(inchesDouble);
            conversionResult[0] = "CONVERSION RESULT: " + metersToConvertString + " m = " + inchesString + " inches";
            conversionResult[1] = "Do you want to convert another number?";
        } catch (NumberFormatException nfe) {
            if (nfe.getMessage().equals("empty String")) {
                conversionResult[0] = "CONVERSION FAILED: " + "You forgot to specify the number of meters.";
            } else {
                conversionResult[0] = "CONVERSION FAILED: " + "\"" + metersToConvertString + "\" is not a number.";
            }
            conversionResult[1] = "Do you want to try once more?";
        }
        return conversionResult;
    }

    public static void main (String[] args) throws IOException {
        String s;
        System.out.println("METERS TO INCHES CONVERTER WELCOMES YOU!");
        do {
            BufferedReader buff = new BufferedReader(
            new InputStreamReader(System.in));
            System.out.print("Enter the number of meters (m) to convert into inches: ");
            System.out.flush();
            s = buff.readLine();
            String result[] = convertMetersToInches (s);
            System.out.println(result[0]);
            System.out.print(result[1] + " (Type \"y\" for \"yes\", any other symbol for \"no\".): ");
            System.out.flush();
            s = buff.readLine();
        } while (processRepeatConversion(s));
        System.out.println("THANKS FOR USING OUR CONVERTER. BYE!");
    }

}

Take a quick look at the code to see which language constructs you can potentially search for. Note that:

  • There are three methods in the class (processRepeatConversion(), convertMetersToInches() and main()).
  • One of the methods returns an array of string values (convertMetersToInches()).
  • There are two if-then-else blocks. One of these blocks includes the else block while the other doesn't.

2. Close the file by clicking File:I12CloseEditorTabIcon.png on the editor tab or pressing CTRL+F4.

File:I12SSR09ClassInEditor.png

Searching for classes. Learning the basic features of the UI

In this section, we show how to access the Structural Search dialog and briefly discuss its layout and main features. Then, we perform the most basic search (the search for classes) using one of the predefined templates (sections "Specifying a search template" and "Performing the search") and discuss the results (sections "Exploring the search results" and "How access modifiers affect the search for classes"). We also show how to jump to the source code for a found code fragment and repeat the search for the refined conditions.

Accessing the Structural Search dialog

The Structural Search dialog provides an access point to structural search in IntelliJ IDEA. In this dialog you define what you want to find and where, and also specify various search options such as case sensitivity, recursion and the like.

To open the Structural Search dialog, select Edit | Find | Search Structurally. (Alternatively, press CTRL+SHIFT+S.)

File:I12SSR10EditFindSearchStructurally.png

Overview of the Structural Search dialog

Prior to performing our first structural search let's take a quick look at the Structural Search dialog.

When you first open the dialog, it looks something like this:

File:I12SSR11StructuralSearchInitView.png

  • The area under Search template is where you specify the search template.
  • The buttons underneath this area let you access various dialogs for working with the templates and template variables.
  • The Recursive matching and Case sensitive options are initially selected. However, IntelliJ IDEA may automatically change these settings as soon as you select a search template. During all our search exercises, both these options will stay deselected.
  • The File type option, among other things, defines the language context for the template being used. That is, the template is validated using the rules of the language selected in the File type list. If java is selected, the template has to be a syntactically valid Java construct. The same applies to other file types (html, xml, etc.).
  • Scope. In this tutorial we will search in the broadest scope possible (that is, in all project files). Our project is very small so we are not going to anyhow constrain the search scope.
  • Status. Unless the search template is a valid language construct, you’ll see the warning This pattern is malformed and won’t be able to perform the search.

If you need more information about this dialog, click the Help button to see the dialog description in IntelliJ IDEA Help.

Specifying a search template

Our first search will be a very basic one. We will search for Java classes existing in our project.

The easiest and most effective way to specify a search template is to use one of the predefined templates. As far as Java is concerned, there's a lot of predefined templates and it's very unlikely that you need more.

To use one of the predefined templates (in this exercise we will use the predefined template that finds all Java classes), do the following:

1. Click Copy existing template.

File:I12SSR11StructuralSearchCopyTemplate.png

The Existing Templates dialog is displayed. A categorized list of existing templates is shown in the left-hand part.

2. In the list of the templates, under class-based, select classes.

File:I12SSR12ExistingTemplatesClasses.png

The selected template is shown in the right-hand part of the dialog under Template preview.

Note the template structure. There are two distinct parts in the template, that is, fixed and variable part. The fixed (constant) part is represented by the keyword class and the curly brackets {}. The variable part is $Class$. In this template, $Class$ is a variable that represents the name of the class.

3. Click OK in the Existing Templates dialog.

You are back to the Structural Search dialog. Note that the Recursive matching and Case sensitive options have automatically been deselected. IntelliJ IDEA has provided the most appropriate settings for the chosen template.

Performing the search

Note the template status in the bottom-left corner of the Structural Search dialog. The absence of a warning means that there's nothing wrong with the template. The template is a syntactically valid Java construct. So all you have to do to perform the search is to click Find.

File:I12SSR13StructuralSearchClasses.png

The Find tool window is displayed showing the search results.

Exploring the search results

Shown in the Find window are the search template used (under Targets) and the search results (under Found usages).

File:I12SSR14ClassFound.png

A found usage, that is, a code fragment satisfying the search conditions, is represented by one line of code (public class MetersToInchesConverter {). In addition, the corresponding file name (MetersToInchesConverter) and the module name (SSRDemo) are shown.

The numbers in brackets ((5:14)) show where in the corresponding file the found usage occurs. (The first of the numbers is the line number; the second is the character number within this line.)

Additional details about the found code fragment are available in the Preview pane. If this pane is not currently shown, click the Preview Usages icon File:I12PreviewUsagesIcon.png on the toolbar of the Find window. (In the same way you can also hide this pane.)

File:I12SSRFindToolbarPreviewUsages.png

The Preview pane lets you see the code fragment in the source file where it is found.

Normally, the first line of the code fragment is highlighted yellow. Also, what is called the search target, is highlighted violet. In this case, this is the class name MetersToInchesConverter.

File:I12SSR15ClassPreview.png

Jumping to the source code for the current usage

In cases when you want to work with the found code fragment in the editor, you can jump to the source code right from the Find tool window.

Double-click (5:14) public class MetersToInchesConverter {.

As a result, the file MetersToInchesConverter.java is opened in the editor and the cursor is placed in front of the class name (MetersToInchesConverter). Note that this position corresponds to the beginning of the fragment highlighted violet in the Preview pane.

File:I12SSR16ClassInEditor.png

You can also jump to the source code using the keyboard or the context menu (close the file MetersToInchesConverter.java and try both of the following methods):

  • Select the usage of interest and press F4.
  • Right-click the usage (1) and click Jump to Source (2).

File:I12SSR17UsageJumpToSource.png

How access modifiers affect the search for classes

You may have not noticed, but access modifiers in the search for classes do not matter unless specified explicitly in the template. (The same applies to fields and methods.) To check that this is the case, perform the searches for the following two templates:

  • public class $Class$ {}. As before, you will find the class MetersToInchesConverter.
  • private class $Class$ {}. You won't find anything.

See the next section for the basic outline of the procedure to be used.

Refining the search conditions

Situations are possible when you don't find what you wanted in one go. In such cases you will probably slightly change the search conditions and repeat the search. Here are the instructions for this procedure:

1. In the lower-left part of the Find window, click Edit Query.

File:I12SSR15FindEditQuery.png

2. In the Structural Search dialog, change the search conditions (for example, modify the template, or other settings) and click Find.

Later in this tutorial, you will use this procedure many times.

Searching for methods. Working with search targets and text constraints

In this section we perform a more advanced search, the search for methods. We show how search targets and text constraints for variables affect the results (sections "Changing the search target", "Specifying a text constraint for a variable", "Inverting a text constraint for a variable" and "Changing the text constraint for a variable").

Performing an initial search

Now we are going to search for methods. As before, we'll choose an appropriate predefined template and use it as-is:

1. In the Find tool window, click Edit Query.

2. In the Structural Search dialog, click Copy existing template.

File:I12SSR18StructuralSearch.png

3. In the Existing Templates dialog, under class-based, click methods of the class, and then click OK.

File:I12SSR19ExistingTemplatesMethods.png

4. In the Structural Search dialog, click Find.

The search results are shown in the Find tool window. All three methods defined in the MetersToInchesConverter class are found.

Note that now the names of the methods are highlighted in the Preview pane.

File:I12SSR20MethodsFound.png

Also note that when you jump to the source code, the cursor is positioned right in front of the method names.

File:I12SSR21MethodInEditor.png

In a minute, we will change the search target and see what changes. Before that, however, let's learn a bit more about navigation.

Jumping to the source code for the next or previous usage

In the Find window, you can move to the next or previous of the found usages using the Next Occurrence File:I12ArrowDownIcon.png and the Previous Occurrence File:I12ArrowUpIcon.png icons on the toolbar.

File:I12SSRFindToolbarUpDown.png

The keyboard shortcuts for these functions are CTRL+ALT+DOWN ARROW and CTRL+ALT+UP ARROW respectively.

Let's see how this works.

While static String[] convertMetersToInches (String metersToConvertString) { is selected in the Find tool window, click Next Occurrence File:I12ArrowDownIcon.png. (Alternatively, press CTRL+ALT+DOWN ARROW.)

As a result:

  • The next of the found usages is selected in the Find tool window (public static void main (String[] args) throws IOException {)
  • The declaration of the main method is shown in the Preview pane.
  • The file MetersToInchesConverter.java opens in the editor and the declaration of the main method is shown there.

If you now press F4, the cursor is placed in the editor in front of the method name main.

Changing the search target

To change the search target for the current search template:

1. In the Find tool window, click Edit Query.

2. In the Structural Search dialog, click Edit variables.

File:I12SSR22StructuralSearchMethods.png

3. In the Edit Variables dialog, under Variables, select MethodName (1).

Note that the check box next to This variable is target of the search is selected. Currently, the template variable MethodName is the search target. This is why the method names (rather than anything else) were highlighted in the Preview pane after the previous search.

4. Deselect this check box (2).

File:I12SSR23EditVariablesMethodName.png

Now we are going to make the variable ReturnType the search target.

5. Click ReturnType (1) and select the check box next to This variable is target of the search (2).

File:I12SSR24EditVariablesReturnType.png

6. Click OK in the Edit Variables dialog.

7. In the Structural Search dialog, click Find.

As before, the three methods are found. Note, however, that now the return type is highlighted for each of the methods in the Preview pane.

File:I12SSR25FoundMethods.png

If you jump to the source code, the cursor is positioned in front of the method return type rather than its name.

File:I12SSR26ReturnTypeInEditor.png

Now let's see how text constraints for variables work.

Specifying a text constraint for a variable

In this section we'll show how to change the search settings so that only the methods that return the String type are found. To do that, we will specify the appropriate text constraint for the variable ReturnType:

1. In the Find tool window, click Edit Query.

2. In the Structural Search dialog, click Edit variables.

File:I12SSR27StructuralSearchMethods.png

3. In the Edit Variables dialog, select ReturnType (1).

4. In the Text/regexp field, type string.* (2).

This pattern specifies a text fragment in which string is followed by any number of any characters. (The dot (.) means any character; the asterisk (*) stands for any number including zero.)

File:I12SSR27ReturnTypeString.png

5. Click OK.

6. In the Structural Search dialog, click Find.

Look at the result in the Find window. Note that now only one method (ConvertMetersToInches()) is found. This is fine because this is the only method in our project whose return type is String.

File:I12SSR28StringMethodFound.png

Now let's search for the methods whose return types are anything other than String.

Inverting a text constraint for a variable

In the previous exercise we constrained the variable ReturnType so that only the methods with the return type String are found. To find the methods that return anything other than String we will logically negate, or, in other words, invert the text constraint for ReturnType:

1. In the Find window, click Edit Query.

2. In the Structural Search dialog, click Edit variables.

3. In the Edit Variables dialog, select ReturnType (1), and select the Invert condition check box (2).

As already mentioned, the Invert condition option is equivalent to logical negation. So we are going to search for the return types that don't match the pattern string.*.

File:I12SSR29EditVariablesInvertCondition.png

4. Click OK.

5. In the Structural Search dialog, click Find.

Note that now two methods are found, and none of these methods returns String: the main() method does not return any value, and the return type of processRepeatConversion() is boolean.

File:I12SSR30NonStringMethodsFound.png

Now to complete the discussion of the text constraints and also for additional exercising, let's search for the methods that return an array.

Changing the text constraint for a variable

To find the methods that return an array, we will appropriately redefine the text constraint for the ReturnType variable:

1. In the Find window, click Edit Query.

2. In the Structural Search dialog, click Edit variables.

3. In the Edit Variables dialog, select ReturnType (1) and clear the Invert condition check box (2).

4. In the Text/regexp field, type .*\[\] (3).

This pattern defines a string in which any number of any characters is followed by []. (The opening and the closing square brackets ([]) in the text constraints both have special meanings. To be treated literally, the special meanings of the square brackets have to be cancelled, or escaped by means of the backslashes (\).)

File:I12SSR31EditVariablesArrayMethods.png

5. Click OK.

6. In the Structural Search dialog, click Find.

Note that one method is found (convertMetersToInches()). This is the only method in our project that returns an array.

File:I12SSR32ArrayMethodFound.png

Searching for if-then-else. Working with the occurrence count settings

In this section we search for if-then-else statements and show how the occurrence count settings affect the search results.

Performing an initial search for if-then-else

Let us start with the predefined template and see what search results it produces:

1. In the Find tool window, click Edit Query.

2. In the Structural Search dialog, click Copy existing template.

File:I12SSR33StructuralSearchMethods.png

3. In the Existing Templates dialog, under operators, selects if’s and click OK.

File:I12SSR33ExistingTemplatesIfs.png

4. In the Structural Search dialog, click Find.

Note that two if-then statements are found. The first of the statements contains the else block while the other doesn't.

File:I12SSR34FirstFoundIf.png

File:I12SSR35SecondFoundIf.png

Let us find out why this is so.

Changing the occurrence count settings

The previous search result has to do with how the occurrence count parameters for the variable ElseStatement are set. Now we are going to look at the corresponding settings and change them to get rid of the if-then statements without the else block:

1. In the Find tool window, click Edit Query.

2. In the Structural Search dialog, click Edit variables.

3. In the Edit Variables dialog, select ElseStatement. Note the settings for the occurrence count.

These settings currently specify that the else block containing from 0 to an unlimited number of else-then statements satisfy the search conditions.

File:I12SSR36EditVariablesElseInitOccurrenceCount.png

By default, the minimum occurrence count for ElseStatement is zero. This means that the if-then-else statements in which there is the else block but no else-then statements (for example, else {}) satisfy the search conditions. On the other hand, the absence of the else-then statements is semantically equivalent to the absence of the whole else block. That is why the if-then-else statement without the else block was found in the previous search.

Let's set the minimum count to 1. By doing so we specify that the else block should contain at least one else-then statement.

4. Type 1 in the Minimum count field and click OK.

File:I12SSR37EditVariablesElseMinOne.png

5. In the Structural Search dialog, click Find.

Note that now only the if-then-else statement with the else block is found.

File:I12SSR38IfWithElseFound.png

To complete our exercises let us show how to modify and save a search template.

Modifying and saving a search template

In this section we modify the if-then-else template so that it finds only the if-then statements without the else block. Then we save the modified template and show where you can find it.

Modifying a search template

To modify the search template:

1. In the Find window, click Edit Query.

2. In the Structural Search dialog, under Search template, delete the part corresponding to the else block:

else {
  $ElseStatement$;
}

Finally, the template should look as shown on the following picture:

File:I12SSR39StructuralSearchIfWithoutElse.png

3. Click Find to see what can be found using this template.

Note that only the if-then statement without the else block is found.

File:I12SSR40IfWithoutElseFound.png

Let's save this template to be able to use it in the future.

Saving a search template

To save the template:

1. In the Find tool window, click Edit Query.

2. In the Structural Search dialog, click Save template.

File:I12SSR41StructuralSearchSaveTemplate.png

3. In the Save template dialog, under Template name, type the name for your new template (for example, if without else). Then click OK.

File:I12SSR42SaveTemplate.png

4. Now to see this new template in the list of existing templates, click Copy existing template.

File:I12SSR43StructuralSearchAccessListOfTemplates.png

The Existing Templates dialog opens. Note that the template you have just saved (if without else) appears in the user defined category.

File:I12SSR44ExistingTemplatesNewTemplate.png

Note: To be able to find the if-then statements without the else block, we, in fact, didn't have to modify the predefined if-then-else template. We could have just set both the minimum and the maximum occurrence counts for the variable ElseStatement to zero. You can now check that as an additional exercise.

Personal tools