Locating data

A basic need of data applications is to find specified data. With the JBCL, you can perform two types of locates:

Locating data with the LocatorControl

The JBCL includes a LocatorControl component that provides locate functionality in a user-interface control. The LocatorControl includes an incremental search feature for String type columns. Its columnName property specifies which column to perform the locate in. If not set, the locate is performed on the last column visited in a UI control (for example the GridControl).

If you include the JBCL StatusBar component in your application, LocatorControl prompts and messages are displayed on the status bar. Connect both the StatusBar and the LocatorControl to the same DataSet component to enable this feature.

The samples\borland\samples\tutorial\dataset\Locator directory of JBuilder includes a finished example of an application that uses the LocatorControl under the project name Locator.jpr. This sample shows how to set a particular column for the locate operation as well as using a TextFieldControl component to prompt the user for the column to locate in. The completed application (with the application window reduced in size) looks like this:

To create this application,

  1. Create a new project and application files using the Project and Application Wizards. Select the Generate status bar and Center frame on screen options from Step 2 of the Application Wizard.

  2. Open the Designer by highlighting the Frame file and selecting the Design tab at the bottom of the AppBrowser.

  3. Add the following components to the Frame. They are available from the Data Express tab of the Component Palette.

  4. Add the following components to the Frame class. They are available from the JBCL tab of the Component Palette.

    Check the screen shot of the running application (shown above) to see the approximate positioning of each component.

Set the properties of the components as follows:

  1. Set the connection property of the Database component to the InterBase sample files as described in Setting Database connection properties.

  2. Set the query property of the QueryDataSet component as follows:
    Property name Value
    Database database1
    Query String select * from employee

  3. Set the dataSet property of the LocatorControl, GridControl, and StatusBar to queryDataSet1.

  4. Select the TextFieldControl. Select the Events tab of the Inspector. Select the TextFieldControl component's keyPressed() event and add the following code to allow the user to specify which column to locate in.
       if (e.getKeyCode() == KeyEvent.VK_ENTER) {
          locatorControl1.setColumnName(textField1.getText());
          locatorControl1.requestFocus();
       }	
    This code tests for the Enter key being pressed. If it determines that the Enter was pressed, the columnName property for the LocatorControl is set to the column named in the TextFieldControl. This instructs the LocatorControl to perform locates in the specified Column. Focus is then shifted to the LocatorControl so that you can enter the value to search for.

  5. Set the text property of the LabelControl that you placed beside the TextFieldControl if you want to prompt the user for the column to locate on:

    Column to locate

    Alternatively, if you want to locate only in a particular Column, set the LocatorControl component's columnName property to the DataSet column you want to locate on, for example, LAST_NAME.

  6. Set the text property of the LabelControl that you placed beside the LocatorControl to: Value to locate

    Note: See the screen shot of the running application earlier in this section for additional instructional text.

  7. Run the application.

When you run the application, you'll notice the following behavior:

Locating data programmatically

This section explores the basics of locating data programmatically as well as conditions which affect the locate operation.

When programmatically locating data:

  1. Instantiate a DataRow based on the DataSet you want to search. If you don't want to search on all columns in the DataSet, create a "scoped" DataRow (a DataRow that contains just the columns for which you want to specify locate values).(See Locating data using a DataRow.)

  2. Assign the values to locate in the appropriate columns of the DataRow.

  3. Call the locate(ReadRow, int) method, specifying the location options you want as the int parameter. Test the return value to determine if the locate succeeded or failed.

  4. To find additional occurences, call locate() again, specifying a different locate option, for example, Locate.NEXT or Locate.LAST. See Locate class variables for information on all the Locate options.

The core locate functionality uses the locate(ReadRow, int) method. The first parameter, ReadRow, is of an abstract class type. Normally you use its (instantiatable) subclass DataRow class. The second parameter represents the locate option and is defined in Locate variables. The Locate class variables represent options that let you control where the search starts from and how it searches, for example with or without case sensitivity. (For more information on locate options, see Working with locate options.) If a match is found, the current row position moves to that row. All data-aware controls that are connected to the same located DataSet navigate together to the located row.

The Locate() method searches within the current view of the DataSet. This means that rows excluded from display by a RowFilterListener are not included in the search.

The view of the DataSet can be sorted or unsorted; if it is sorted, the locate() method finds matching rows according to the sort sequence.

To locate a null value in a given column of a DataSet, include the column in the DataRow parameter of the locate() method but do not assign it a value.

Tip: If the locate() method fails to find a match when you think it should succeed, check for null values in some columns; remember that all columns of the DataRow are included in the search. To prevent this, use a "scoped" DataRow containing only the desired columns.

Locating data using a DataRow

A DataRow is similar to a DataSet in that it contains multiple Column components. However, it stores only one row of data. You specify the values to locate for in the DataRow.

When the DataRow is created based on the same located DataSet, the DataRow contains the same column names and data types and column order as the DataSet it is based on. All columns of the DataRow are included in the locate operation by default; to exclude columns from the locate, create a "scoped" DataRow that contains only specified columns from the DataSet. You create a "scoped" DataRow using either of the following DataRow constructors:

Both the DataRow and the DataSet are subclasses of ReadWriteRow. Both inherit the same methods for manipulation of its contents, for example, getInt(String), and setInt(String, int). You can therefore work with DataRow objects using many of the same methods as the DataSet.

Working with locate options

You control the locate operation using locate options. These are constants defined in the borland.jbcl.dataset.Locate class. You can combine locate options using the bitwise OR operator; several of the most useful combinations are already defined as constants. Four of the locate options (FIRST, NEXT, LAST, and PRIOR) determine how the rows of the DataSet are searched. The CASE_INSENSITIVE and PARTIAL) options define what is considered a matching value. The FAST constant affects the preparation of the locate operation.

You must specify where the locate starts searching and which direction it moves through the rows of the DataSet. Choose one of the following:

If one of these constants is not specified for a locate operation, a DataSetException of NEED_LOCATE_START_OPTION is thrown.

To find all matching rows in a DataSet, call the locate() method once with the locate option of FIRST. If a match is found, re-execute the locate using the NEXT_FAST option, calling the method with this locate option repeatedly until it returns false. The FAST locate option specifies that the locate values have not changed, so they don't need to be read from the DataRow again. To find all matching rows starting at the bottom of the view, use the options LAST and PRIOR_FAST instead.

The CASE_INSENSITIVE option specifies that string values are considered to match even if they differ in case. Specifying whether a locate operation is CASE_INSENSITIVE or not is optional and only has meaning when locating in String columns; it is ignored for other data types. If this option is used in a multi-column locate, the case sensitivity applies to all String columns involved in the search.

The PARTIAL option specifies that a row value is considered to match the corresponding locate value if it starts with the first characters of the locate value. For example, you might use a locate value of "M" to find all last names that start with "M". As with the CASE_INSENSITIVE option, PARTIAL is optional and only has meaning when searching String columns.

Multi-column locates that use PARTIAL differ from other multi-column locates in that the order of the locate columns makes a difference. The constructor for a scoped, multi-column DataRow takes an array of column names. These names need not be listed in the order that they appear in the DataSet. The PARTIAL option applies only to the last column specified, therefore, control over which column appears last in the array is important.

For a multi-column locate operation using the PARTIAL option to succeed, a row of the DataSet must match corresponding values for all columns of the DataRow except the last column of the DataRow. If the last column starts with the locate value, the method succeeds. If not, the method fails. If the last column in the DataRow is not a String column, the locate() method throws a DataSetException of PARTIAL_SEARCH_FOR_STRING.

Locates that handle any data type

Data stored in JBCL Data Access components are stored in using Variant objects. When data is displayed, a String representation of the variant is used. To write code that performs a generalized locate that handles columns of any data type, use one of the setVariant() methods and one of the getVariant() methods.

For example, you might want to write a generalized locate routine that accepts a value and looks for the row in the DataSet that contains that value. The same block of code can be made to work for any data type because the data stays a variant. To display the data, use the appropriate formatter class in the JBCL model package (or create your own custom formatter).

Column order in the DataRow and DataSet

While a Column from the DataSet can only appear once in the DataRow, the column order may be different in a scoped DataRow than in the DataSet. For some locate operations, column order can make a difference. For example, this can affect multi-column locates when the PARTIAL option is used. For more information on this, see the paragraph on multi-column locates with the PARTIAL option earlier in this topic.