Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Constraining input data 1

Status
Not open for further replies.

EdwardJS024

Programmer
Nov 24, 2004
82
US
Maybe you guys can lend hand in an intuitive way to handle input validation from the client/UI.


I am working on a validation engine that validates all input data and making sure it meets a domain specification/criteria, only then is it considered a valid input.


PROBLEM:++++++++++++++++++++++++++++++++++++++++++++++++++
My only concern is, what is the best way to notify
the client that their input is invalid AND one or more
fields may have been the cause? I also want to give a
sumamry of the fields that is not valid.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++





DESIGN:++++++++++++++++++++++++++++++++++++++++++++++++++++
So, i decided to write Specifications that represent
fields on the UI that map to objects of the domain (yet another reason why storage persistence and UI are just export mediums, they work alike):

So:
AddressSpecification
AgeSpecification
PhoneSpecification
FirstNameSpecification
etc..
etc..



Clients can use these primitive specs in order to validate fields before submission. I then realized that maybe a composite pattern will fit nicely so i created a
CustomSpecification that represents an entire form that needs to be validated.

CustomSpecification accepts constraints via:
aCustomSpecification.Add(aSpec);

calling CustomSpecification.IsValid() will dispatch the request to all children checking for validity.


If one or more fields fail, i want to let the clients know 2 things:

1. IsValid is false
2. a summary of fields that failed (a collecton/list)?

I can't seem to put it together, i have a general idea but hopefully someone has done this before?


-EJS
 
What I've done is create a small class that is the validation status for each data point. It can have one of several validation failure statuses:
- Out of range
- Referential integrity constraint failed
- key violation
- Size violation
You would include the name of the data point, as well as a short description of the problem.

I build up a collection of these objects as I validate the data, returning it to the caller with a "data is bad, and here is why" status code.

Chip H.


____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
EdwardJS024, I have worked on the same question. In the beginning I went on a solution that looked similar to yours. I found it too heavy to implement and moved to another lighter one.

I defined a generic class `Editor'. Instances of Editor are widgets specialized in the edition of objects whose type is the generic parameter. Here is the class with some contracts, written in C++ alike :
Code:
template<class G>
class Editor : public Widget
{
protected:
  //! The first member contains the result of an assertion and the second
  //! the error to display if the assertion failed.
  typedef Pair<Boolean, String> CheckStructure;

  // ------------------- Initialization and Deletion ---------------------
public:
  //! Construct an instance of Editor<G>.
  Editor<G> (Widget* parent) :
    Widget (parent)
  {
  }

  //! Destruct the instance and free any allocated resources.
  virtual ~Editor<G> ()
  {
  }

  // ----------------------------- Access --------------------------------
public:
  //! Result of the edition.
  G* result ()
  {
    require (is_valid_edition ());//valid_edition

    return impl_do_result ();
  }

  // -------------------------- Status report ----------------------------
public:
  //! Is the current edition valid(default: true)? Fill `errors' if not
  //! Void.
  virtual Boolean is_valid_edition (Collection<String>* errors = Void) const
  {
    return true;
  }

  // -------------------------- Element change ---------------------------
public:
  //! Set the contents of this Editor object to display `v'.
  virtual void install (const G& v) = 0;

  // -------------------------- Implementation ---------------------------
protected:
  //! The deferred body of the function result().
  virtual G* impl_do_result () = 0;

  //! Is `assertion' true? If not, extend `errors' with `error'.
  Boolean check (Boolean assertion, const String& error_text,
		 Collection<String>* errors = Void) const
  {
    if (not to_check and errors != Void)
      errors->extend (error);
    return to_check;
  }

  //! Is each assertion in `to_check' satisfied? If not, extend errors with
  //! the associated error description.
  Boolean check_list (const Sequence<CheckStructure>& to_check,
		      Collection<String>* errors = Void) const
  {
    Boolean Result = true;
    ConstIterator<CheckStructure> icheck = to_check.const_iterator ();
    until (icheck->after ())
      {
	Result = check (icheck.item ().first (),
			icheck.item ().second (), errors) and Result;
	icheck->forth ();
      }
    return Result;
  }

};

You can define classes IntegerEditor, RealEditor, ColorEditor, etc. that inherit from this class.
A CustomerEditor class would inherits from Editor<Customer> and built with sub editors that do the validation for the age(an IntegerEditor), the first name(a StringEditor), and so on.

To illustrate how to make an editor for objects of a peculiar type, here is a simplified IntegerEditor class :
Code:
class IntegerEditor : public Editor<Integer>
{
public:
  //! Integer editors that accept any integer input.
  IntegerEditor ()
  {
    _min = negative_infinity ();
    _max = positive_infinity ();
    _line_editor = new LineEditWidget (this);
  }

  //! Integer editors that accept input integers in the range [min, max].
  IntegerEditor (Integer min, Integer max)
  {
    _min = min;
    _max = max;
    _line_editor = new LineEditWidget (this);
  }

public:
  //! Synonym of result().
  Integer value ()
  {
    return *result ();
  }

  // -- from Editor.
  Boolean is_valid_edition (Collection<String>* errors = Void) const
  {
    Integer value = to_integer (_line_editor->text ());

    Collection<CheckStructure> checks;
    checks.extend (CheckStructure (is_number (_line_editor->text ()),
				"The text entered is not an integer"));
    checks.extend (CheckStructure (_min <= value and value <= _max,
				"Entered integer is not in range"));
    return check_list (checks, errors);
  }

  // -- from Editor.
  void install (const Integer& v)
  {
    _line_editor->set_text (to_string (v));
  }

protected:
  //! -- from Editor.
  Integer* impl_do_result ()
  {
    _result = to_integer (_line_editor->text ());
    return &_result;
  }

private:
  Integer _min;
  Integer _max;
    
  Integer _result;
  LineEditWidget* _line_editor;
};

An example of use :
Code:
IntegerEditor age_editor = new IntegerEditor (0, 130);
age_editor.install (customer.age ());

//the user modifies the age displayed through the UI...

Collection<String> errors;
if (not age_editor.is_valid_edition (&errors))
  // display to the user the errors
else
  // valid case :
  customer.set_age (age_editor.value ());

Does it meet your needs?

--
Globos
 
Hi Chip,
I took your feedback into consderation and started some research. I discovered that the better way of returning different error values/codes is not too at all. Instead rely on the objects to throw exceptions if a criteria is being violated.. Thiose exceptions can then be caught by objects/layers higher on the chain and handled accordingly (notifying users, listing reasons in a UI Label, logging, etc)
What are your thoughts on this?


Globos,
Your solution is pretty neat if there were strictly UI validation. In my case i am writing a set of programatic validators that can be used to validate webservice params, querystrings, cookies etc. So not necessarily the GUI all the time.

 
Exceptions are goodness, and I use them all the time. But my assumption was that the validation would be occuring at a layer below the presentation layer. And since I don't like to throw exceptions across layer boundaries (since different layers could be written in different languages), I usually go back to returning a data structure. This way I treat each layer boundary as an API to ensure I get good separation between them.

Chip H.



____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
And since I don't like to throw exceptions across layer boundaries (since different layers could be written in different languages), I usually go back to returning a data structure"


can you elaborate on your feelings about throwing exceptions across boundries? Aside from languages differences what else is a problem here?
 
The physical structure of an exception object is specific to the language/runtime used. A lot of the time, an exception will work across a boundary because the same technology is used on both sides (java/java .net/.net, etc). True, sometimes compatability libraries are available (.net to COM being a good example), but often they aren't.

Also - since none of us can predict the future, I've found it best to stick to a lowest-common-denominator solution when crossing a boundary.

At my last job, in the space of 18 months, we went from VB6 COM to C# .NET to J2EE with a Flash-based UI. Once we all recovered from the whiplash, we found that we couldn't re-use our VB6 backend without a rewrite because the Java to COM bridge wasn't reliable and didn't scale that well.

I was laid off from them in January, and the last I heard, they were still working on duplicating the 30-man-years worth of work that had been done in VB6, but in Java, and with no end in sight.

Chip H.


____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
The physical structure of an exception object is specific to the language/runtime used"

Why is that? Isn't an exception just another object? Why is it specific to a runtime? Maybe I am missing the point.

Also, if you don't throw exceptions across layer boundries, how else do you bubble up an error from the DAL to the client/UI?

EJS
 
Exceptions are *sometimes* just another object. Under C++, they're not -- they're a language construct.

To pass across a layer boundary I'll either return a status code, or return an object which has detailed information about what went wrong.

Chip H.


____________________________________________________________________
If you want to get the best response to a question, please read FAQ222-2244 first
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top