diff --git a/fapi_validation.module b/fapi_validation.module
index 999fde4dde35332de9296686190a08512eb1a5fb..43eba43cc1bc88f98d676aa287818ecce45c25f1 100644
--- a/fapi_validation.module
+++ b/fapi_validation.module
@@ -1,79 +1,60 @@
-<?php
+ <?php
 
 /**
- * Implementation of hook_form_alter
+ * Implements hook_element_info_alter().
  */
-function fapi_validation_form_alter(&$form, $form_state, $form_id) {
-  if (!isset($form['#validate']) || !is_array($form['#validate'])) {
-    $form['#validate'] = array();
+function fapi_validation_element_info_alter(&$types) {
+  foreach ($types as $type_name => $type_info ) {
+    if (!empty($type_info['#input'])) {
+      $types[$type_name]['#process'][] = 'fapi_validation_element_process';
+    }
   }
-
-  $form['#validate'] = array_merge(array('fapi_validation_form_validate'), $form['#validate']);
 }
 
 /**
- * Validate the form searching for #rules and #filters params.
+ * Process element rules and filters.
  */
-function fapi_validation_form_validate(&$form, &$form_state) {
-  // Initializing register
-  fapi_validation_register($form, NULL, $form_state);
-  // Starting validation process
-  _fapi_validation_element_validate($form, $form_state);
-  // Updating $form_state
-  $form_state = fapi_validation_register($form);
-}
-
-function _fapi_validation_element_validate(&$elements, $child=FALSE) {
-  if (isset($elements['#filters'])) {
-    fapi_validation_filters_execute($elements);
+function fapi_validation_element_process($element, &$form_state) {
+  if (!empty($element['#filters'])) {
+    $element_validate = empty($element['#element_validate']) ? array() : $element['#element_validate'];
+    $element['#element_validate'] = array_merge(array('fapi_validate_element_filter'), $element_validate);
   }
-
-  if (isset($elements['#rules'])) {
-    fapi_validation_rules_execute($elements);
-  }    
-  
-  $values = array();  
-  foreach (element_children($elements) as $key) {
-    _fapi_validation_element_validate($elements[$key], TRUE);
+  if (!empty($element['#rules'])) {
+    $element['#element_validate'][] = 'fapi_validate_element_validate';
   }
-  
-  return $values;
+  return $element;
 }
 
-function fapi_validation_register(&$element=NULL, $value=NULL, $form_state=NULL) {
-  static $fs;
-  
-  if (!is_null($form_state)) {
-    $fs = $form_state;
-  }
-  
-  if (!is_null($element) && !is_null($value)) {
-    form_set_value($element, $value, $fs);
-    $element['#value'] = $value;
-  }
-  else {
-    return $fs;
-  }
+/**
+ * Run element filter callbacks.
+ */
+function fapi_validate_element_filter(&$element, &$form_state) {
+  fapi_validation_filters_execute($element, $form_state);
 }
 
-function fapi_validation_filters_execute(&$element) {
+/**
+ * Run element validation callbacks.
+ */
+function fapi_validate_element_validate(&$element, &$form_state) {
+  fapi_validation_rules_execute($element, $form_state);
+}
+
+function fapi_validation_filters_execute(&$element, &$form_state) {
   $data = _fapi_validation_data('filters');
 
   if (!isset($element['#value'])) {
     return;
   }
 
-  $value = $element['#value'];
   foreach ($element['#filters'] as $filter) {
-    if (!isset($data[$filter])) {
-      continue;
+    if (!empty($data[$filter])) {
+      $element['#value'] = $data[$filter]['callback']($element['#value']);
+      form_set_value($element, $element['#value'], $form_state);
     }
-    $value = $data[$filter]['callback']($value);
   }
-  fapi_validation_register($element, $value);
 }
 
-function fapi_validation_rules_execute(&$element) {
+function fapi_validation_rules_execute(&$element, &$form_state) {
   $data = _fapi_validation_data('rules');
 
   // If element is empty and not required, by pass rule validation.
@@ -82,19 +63,23 @@ function fapi_validation_rules_execute(&$element) {
   }
 
   foreach ($element['#rules'] as $rule) {
-    $params = array($element['#value']);
-    $error_message = NULL;
+    $error_message = $error_callback = NULL;
 
     // If $rule is an array, use error message if is setted.
     if (is_array($rule)) {
+      if (!isset($rule['rule'])) {
+        drupal_set_message(t('Rule array with wrong structure on %field.', array('%field' => $element['#name'])), 'error');
+        continue;
+      }
+
       if (isset($rule['error'])) {
         $error_message = $rule['error'];
       }
 
-      if (!isset($rule['rule'])) {
-        drupal_set_message(t('Rule array with wrong structure on %field.', array('%field' => $element['#name'])), 'error');
-        continue;
+      if (isset($rule['error callback'])) {
+        $error_callback = $rule['error callback'];
       }
+
       $rule = $rule['rule'];
     }
     
@@ -107,6 +92,8 @@ function fapi_validation_rules_execute(&$element) {
       continue;
     }
 
+    $params = array($element['#value']);
+
     // Parsing parameters.
     if (isset($rs[3])) {
       if ($rule == 'regexp') {
@@ -116,11 +103,24 @@ function fapi_validation_rules_execute(&$element) {
         $params[] = preg_split('/ *, */', $rs[3]);
       }
     }
+
+    $params[] = &$element;
+    $params[] = &$form_state;
     
     $ret = call_user_func_array($data[$rule]['callback'], $params);
+
     if (!$ret) {
-      $error = is_null($error_message) ? $data[$rule]['error_msg'] : $error_message;
-      form_set_error($element['#name'], t($error, array('%field' => $element['#title'])));
+      if (!is_null($error_callback)) {
+        $error_params = array(
+          $rule, $params, $element, $form_state
+        );
+        $error = call_user_func_array($error_callback, $error_params);
+      }
+      else {
+        $error = is_null($error_message) ? $data[$rule]['error_msg'] : $error_message;
+        $error = t($error, array('%field' => $element['#title']));
+      }
+      form_set_error($element['#name'], $error);
     }
   }
 }
@@ -195,6 +195,10 @@ function fapi_validation_fapi_validation_rules() {
       'callback' => 'fapi_validation_rule_regexp',
       'error_msg' => t('%field value does not match rule.')
     ),
+    'match_field' => array(
+      'callback' => 'fapi_validation_rule_match_field',
+      'error_msg' => t('%field value does not match other field.')
+    )
   );
 }
 
@@ -244,6 +248,9 @@ function fapi_validation_rule_length($value, $params) {
     return $size == (int) $params[0];
   }
   elseif (count($params) == 2) {
+    if ($params[1] == '*') {
+      return ($size >= (int) $params[0]);
+    }
     return ($size >= (int) $params[0] && $size <= (int) $params[1]);
   }
 }
@@ -291,6 +298,10 @@ function fapi_validation_rule_regexp($value, $params) {
   return (bool) preg_match($params[0], (string) $value);
 }
 
+function fapi_validation_rule_match_field($value, $params, $element, $form_state) {
+  return ($value == $form_state['values'][$params[0]]);
+}
+
 
 /**
  * Filters