Home Page ] [ Eiffel Archive ] [ Eiffel Classes and Clusters ]

Arc de Triomphe clipart (2486 bytes)Input Filter class


Written by Peter Horan (peter@deakin.edu.au).

infilter.zip (3913 bytes)


Description

This class provides filter routines for accepting data from a user in certain domain and rejecting data outside that domain. Using a filter separates the filtering from the use of the data. Thus, the application requiring data input can specify the kind of input and leave it up to the filter to enforce this. That is, the filter allows the application to control the domain of the data before it is received.

                     .--------.  Requested kind of data .------.
  Arbitrary data --->| Filter |<------------------------| User |
                     |        |------------------------>|      |
                     .--------.  Only the right data    .------.

Alternatives to this approach are to accept all input and process it after the fact, or to use a parser to return a datum and its domain. This approach lies between the primitive post-hoc and sophisticated parser approaches.

A Shortcoming

In this version, the class is built using the features of STD_FILES. This has a shortcoming owing to the limited word size used for integers, and the absence (or perhaps my ignorance) of a means to detect overflow and underflow exceptions. This is because 'C' leaves that to the user, which means that one cannot know about such exceptions at the Eiffel end without handling the integer conversions oneself.

Features supplied

The feature last_input holds the most recent character accepted. The feature last_integer holds the most recent integer accepted.

Two routines for filtering integer data are supplied, "read_positive_integer" and "read_integer_between(lower, upper: INTEGER)". These ensure that last_integer >= 0 and lower <= last_integer <= upper respectively.

A routine for accepting input characters which are members of an argument string "read_input_in_string(s: STRING)" is also provided.

Finally, the feature "message_expecting_yes_no(query: STRING): BOOLEAN is supplied. This is built using "read_input_in_string("YyNn")". The pattern here is a chain of filters, the first to restrict the character set to "YyNn" and the second to map the accepted character into a BOOLEAN.

Use of the rescue clause

If a numerical string is expected, starting it with non-numerical characters will cause readint to raise an exception. The rescue
clause allows for this. There seems to be no other way in the circumstances to prevent the exception being raised.

Conclusion

Clearly the idea can be extended. I have used it in complex user interfaces where I wished to restrict the user to certain inputs. I have found the pattern to be robust and easy to maintain. The purpose here is to examine what happens in an OO version of the pattern.

An obvious extension, not done because of time constraints, is to handle the integer conversion in the following way:

read_integer_safely is
   local
        i: INTEGER
    do
       from
            read_input_in_string("0123456789 %T%N")
       until
            last_input = ' ' or
            last_input = '%T' or
            last_input = '%N'
        loop
            i := 10 * i + last_input.code - ('0').code
            read_input_in_string("0123456789 %T%N")
        end
        lastint := i
    end

Whether this should be in a supplier filter (playing the role of "io") or called internally will only be clear from implementation. Precisely how to handle overflow needs to be investigated.

However implemented, the pattern again is a chain of filters. The first restricts the allowed character set and the second builds the integer. A third such as "read_positive_integer" could follow, obtaining save input conversion for the application.

This software is released to the public domain. Please let me know if you have any comments or suggestions for changes.


Home Page ] [ Eiffel Archive ] [ Eiffel Classes and Clusters ]