public function drupalFormValidate($values, $storage)  {
    $this->populateFromDrupalForm($values, $storage);
    list($errors, $warnings) = $this->validate($values);

    // since failed mapping rows in form, don't populate ->ldapUserSynchMappings, need to validate these from values
    foreach ($values as $field => $value) {
      $parts = explode('__', $field);
      // since synch mapping fields are in n-tuples, process entire n-tuple at once (on field == configurable_to_drupal)
      if (count($parts) != 4 || $parts[1] !== 'sm' || $parts[2] != 'configurable_to_drupal') {
        continue;
      }
      list($direction, $discard, $column_name, $i) = $parts;
      $action = $storage['synch_mapping_fields'][$direction][$i]['action'];
      $tokens = array();
      $row_mappings = array();
      foreach (array('remove', 'configurable_to_drupal', 'configurable_to_ldap', 'convert', 'direction', 'ldap_attr', 'user_attr', 'user_tokens') as $column_name) {
        $input_name = join('__', array('sm', $column_name, $i));
        $row_mappings[$column_name] = isset($values[$input_name]) ? $values[$input_name] : NULL;
      }

      $has_values = $row_mappings['ldap_attr'] || $row_mappings['user_attr'];
      if ($has_values) {
        $tokens['%ldap_attr'] = $row_mappings['ldap_attr'];
        $row_descriptor = t("server %sid row mapping to ldap attribute %ldap_attr", $tokens);
        $tokens['!row_descriptor'] = $row_descriptor;
        if (!$row_mappings['direction']) {
          $input_name = join('__', array('sm', 'direction', $i));
          $errors[$input_name] = t('No mapping direction given in !row_descriptor', $tokens);
        }
        if ($direction == LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER && $row_mappings['user_attr'] == 'user_tokens') {
          $input_name = join('__', array('sm', 'user_attr', $i));
          $errors[$input_name] =  t('User tokens not allowed when mapping to Drupal user.  Location: !row_descriptor', $tokens);
        }
        if (!$row_mappings['ldap_attr']) {
          $input_name = join('__', array('sm', 'ldap_attr', $i));
          $errors[$input_name] = t('No ldap attribute given in !row_descriptor', $tokens);
        }
        if (!$row_mappings['user_attr']) {
          $input_name = join('__', array('sm', 'user_attr', $i));
          $errors[$input_name] = t('No user attribute given in !row_descriptor', $tokens);
        }
      }

    }
    return array($errors, $warnings);
  }