diff --git a/addressfield.module b/addressfield.module
index 1f2ff87..a5966c3 100644
--- a/addressfield.module
+++ b/addressfield.module
@@ -133,7 +133,9 @@ function addressfield_generate($address, array $handlers, array $context = array
   if ($context['mode'] == 'form') {
     $format['#addressfield'] = TRUE;
     $format['#process'][] = 'addressfield_process_format_form';
-    $format['#required'] = FALSE;
+    $format['#required'] = $context['instance']['required'];
+    $format['#non_empty_fields'] = $context['instance']['settings']['non_empty_fields'];
+    $format['#non_empty_fields_op'] = $context['instance']['settings']['non_empty_fields_op'];
   }
   elseif ($context['mode'] == 'render') {
     $format['#pre_render'][] = 'addressfield_render_address';
@@ -152,7 +154,28 @@ function addressfield_process_format_form($format, &$form_state, $complete_form)
     ctools_plugin_load_function('addressfield', 'format', $handler, 'format callback');
   }
 
-  _addressfield_process_format_form($format, $format['#address'], $format['#required']);
+  $required = array(
+    'general' => !empty($format['#required']),
+    'non_empty_fields' => $format['#non_empty_fields'],
+    'non_empty_fields_op' => $format['#non_empty_fields_op'],
+  );
+
+  _addressfield_process_format_form($format, $format['#address'], $required);
+
+  if ($required['general']) {
+    $message = t('This field is required.');
+  }
+  else {
+    $message = t('This field is not required.');
+  }
+
+  if ($required['non_empty_fields_op'] == 'or') {
+    $format['#description'] = $message . ' ' . t('However, for the address to be considered populated, one or more fields marked with an asterisk must be populated.');
+  }
+  else {
+    $format['#description'] = $message . ' ' . t('However, for the address to be considered populated, all fields marked with an asterisk must be populated.');
+  }
+
   return $format;
 }
 
@@ -176,7 +199,11 @@ function _addressfield_process_format_form(&$format, $address, $required) {
           $child['#type'] = 'textfield';
         }
       }
-      if (!$required) {
+
+      if (!empty($required['non_empty_fields'][$key])) {
+        $child['#required'] = TRUE;
+      }
+      else {
         unset($child['#required']);
       }
 
@@ -278,6 +305,13 @@ function theme_addressfield_container($variables) {
  * Implementation of hook_element_info().
  */
 function addressfield_element_info() {
+  $types['addressfield'] = array(
+    '#theme_wrappers' => array('fieldset'),
+    '#element_validate' => array('addressfield_element_addressfield_validate'),
+    '#tag' => 'div',
+    '#non_empty_fields' => array('country' => 'country'),
+    '#non_empty_fields_op' => 'or',
+  );
   $types['addressfield_container'] = array(
     '#theme_wrappers' => array('addressfield_container'),
     '#process' => array('addressfield_widget_process'),
@@ -309,7 +343,10 @@ function addressfield_field_info() {
     'label' => t('Postal address'),
     'description' => t('A field type used for storing postal addresses according the xNAL standard.'),
     'settings' => array(),
-    'instance_settings' => array(),
+    'instance_settings' => array(
+      'non_empty_fields' => array('country' => 'country'),
+      'non_empty_fields_op' => 'or',
+    ),
     'default_widget' => 'addressfield_standard',
     'default_formatter' => 'addressfield_default',
     'property_type' => 'addressfield',
@@ -320,6 +357,181 @@ function addressfield_field_info() {
 }
 
 /**
+ * Form API validation function for validating an addressfield element.
+ */
+function addressfield_element_addressfield_validate($element, &$form_state, $form) {
+  // If we are processing the field settings default values form.
+  if (!isset($form['#entity'])) {
+    // Reset the element_key so that the default values will be saved.
+    $form_state['values'][$element['#field_name']][$element['#language']][$element['#delta']]['element_key'] = '';
+  }
+
+  // Keep a pointer to the original $element.
+  $addressfield = &$element;
+
+  // Build an array of references to individual address elements.
+  $children = array(); // Array to hold references to child elements.
+  $stack = array(&$element); // Stack of elements being processed.
+  $ekey = key($stack); // Current element key.
+
+  while (isset($element)) {
+    // If the element has child elements to process.
+    if ($ckey = key($element)) {
+      // If current is a child element.
+      if (element_child($ckey)) {
+        // If the child is an individual address element.
+        if (in_array($ckey, array('name_line', 'first_name', 'last_name', 'organisation_name', 'country', 'administrative_area', 'sub_administrative_area', 'locality', 'dependent_locality', 'postal_code', 'thoroughfare', 'premise', 'sub_premise'))) {
+          // Add the child to the array of children references.
+          $children[$ckey] = &$element[$ckey];
+        }
+
+        // Move to the next child.
+        next($element);
+
+        // If the child has children to process.
+        if (element_children($element[$ckey])) {
+          // Add the child onto the stack for processing its children.
+          $stack[$ckey] = &$element[$ckey];
+          // Make the child the current element.
+          $element = &$element[$ckey];
+          reset($element);
+          $ekey = $ckey;
+        }
+      }
+      else {
+        // Move to the next child.
+        next($element);
+      }
+    }
+    else {
+      // Remove the current element from the stack.
+      array_pop($stack);
+
+      // If there are still elements on the stack.
+      if (!empty($stack)) {
+        // Move processing to the next element on the stack.
+        $ekey = key($stack);
+        $element = &$stack[$ekey];
+      }
+      else {
+        unset($element);
+      }
+    }
+  }
+
+  // Reset element to its original value.
+  $element = &$addressfield;
+
+  // Remove existing errors related to the element.
+  if ($errors = form_get_errors()) {
+    $ekey = $element['#field_name'] . '][' . $element['#language'] . '][' . $element['#delta'];
+    // Clear errors.
+    form_clear_error();
+    // Clear error messages.
+    $error_messages = drupal_get_messages('error');
+    // Initialize an array where removed messages are stored.
+    $removed_messages = array();
+
+    // Remove all errors originated by the element.
+    foreach ($errors as $name => $error_message) {
+      if (strpos($name, $ekey) !== FALSE) {
+        $removed_messages[$name] = $error_message;
+        unset($errors[$name]);
+      }
+    }
+
+    // Reinstate remaining errors.
+    foreach ($errors as $name => $error) {
+      form_set_error($name, $error);
+      // form_set_error() calls drupal_set_message(), so we ahve to filter out
+      // these from the error messages as well.
+      $removed_messages[] = $error;
+    }
+
+    // Reinstate remaining error messages (which, at this point, are messages
+    // that were originated outside of the validation process).
+    foreach (array_diff($error_messages['error'], $removed_messages) as $message) {
+      drupal_set_message($message, 'error');
+    }
+  }
+
+  $valid = $element['#non_empty_fields_op'] == 'or' ? FALSE : TRUE;
+  $default = TRUE;
+  $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
+  foreach ($children as $name => $field) {
+    // Flag if the element is not in a default state.
+    if (!empty($field['#value']) && $field['#value'] != $instance['default_value'][0][$name]) {
+      $default = FALSE;
+    }
+
+    // Determine if non empty field requirements have been met.
+    if ($field['#required']) {
+      if ($element['#non_empty_fields_op'] == 'or' && !empty($field['#value'])) {
+        $valid = TRUE;
+      }
+      elseif ($element['#non_empty_fields_op'] == 'and' && empty($field['#value'])) {
+        $valid = FALSE;
+      }
+    }
+  }
+
+  if ($element['#required']) {
+    if (!$valid) {
+      if ($element['#non_empty_fields_op'] == 'or') {
+        form_set_error($element['#field_name'], t('%title is Required. For the address to be considered populated, one or more fields marked with an asterisk must be populated.', array('%title' => $element['#title'])));
+      }
+      else {
+        form_set_error($element['#field_name'], t('%title is Required. For the address to be considered populated, all fields marked with an asterisk must be populated.', array('%title' => $element['#title'])));
+      }
+    }
+  }
+  else {
+    if (!$valid && !$default) {
+      if ($element['#non_empty_fields_op'] == 'or') {
+        form_set_error($element['#field_name'], t('It appears you have attempted to populate the %title field. For the address to be considered populated, one or more fields marked with an asterisk must be populated. Empty all fields to clear this error.', array('%title' => $element['#title'])));
+      }
+      else {
+        form_set_error($element['#field_name'], t('It appears you have attempted to populate the %title field. For the address to be considered populated, all fields marked with an asterisk must be populated. Empty all fields to clear this error.', array('%title' => $element['#title'])));
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function addressfield_field_instance_settings_form($field, $instance) {
+  $form = array();
+  $options = array();
+
+  foreach($field['columns'] as $name => $info) {
+    $options[$name] = $info['description'];
+  }
+
+  $form['non_empty_fields'] = array(
+    '#title' => t('Required fields for non-empty address'),
+    '#description' => t('Select which fields are required for the address to be considered populated (see the operator field below for specifying whether all selected fields are required, or one or more of the selected fields are required).'),
+    '#options' => $options,
+    '#type' => 'checkboxes',
+    '#default_value' => !empty($instance['settings']['non_empty_fields']) ? $instance['settings']['non_empty_fields'] : array('country' => 'country'),
+    '#required' => TRUE,
+    '#weight' => -2,
+  );
+
+  $form['non_empty_fields_op'] = array(
+    '#title' => t('Operator'),
+    '#description' => t('Determines whether all selected fields are required, or one or more of the selected fields are required.'),
+    '#options' => array('and' => t('All fields are required'), 'or' => t('Only one field is required')),
+    '#type' => 'radios',
+    '#default_value' => !empty($instance['settings']['non_empty_fields_op']) ? $instance['settings']['non_empty_fields_op'] : 'or',
+    '#required' => TRUE,
+    '#weight' => -1,
+  );
+
+  return $form;
+}
+
+/**
  * Returns an array of default values for the addressfield form elements.
  */
 function addressfield_default_values($available_countries = NULL) {
@@ -358,9 +570,28 @@ function addressfield_default_values($available_countries = NULL) {
  * Implements hook_field_is_empty().
  */
 function addressfield_field_is_empty($item, $field) {
-  // Every address field must have at least a country value or it is considered
-  // empty, even if it has name information.
-  return empty($item['country']);
+  $empty = TRUE;
+
+  if (!empty($item['element_key'])) {
+    list($entity_type, $bundle_name, $field_name) = explode('|', $item['element_key']);
+    $instance = field_info_instance($entity_type, $field_name, $bundle_name);
+
+    foreach ($instance['default_value'][0] as $key => $value) {
+      // If the key is an address element.
+      if (in_array($key, array('name_line', 'first_name', 'last_name', 'organisation_name', 'country', 'administrative_area', 'sub_administrative_area', 'locality', 'dependent_locality', 'postal_code', 'thoroughfare', 'premise', 'sub_premise'))) {
+        // If the value is set and it's not equal to the default value.
+        if (isset($item[$key]) && $item[$key] != $value) {
+          $empty = FALSE;
+          break;
+        }
+      }
+    }
+  }
+  else {
+    $empty = empty($item['country']);
+  }
+
+  return $empty;
 }
 
 /**
@@ -461,10 +692,7 @@ function addressfield_field_widget_form(&$form, &$form_state, $field, $instance,
   // select list at the top that reloads the available address elements when the
   // country is changed.
   if ($instance['widget']['type'] == 'addressfield_standard') {
-    // Wrap everything in a fieldset. This is not the best looking element,
-    // but it's the only wrapper available in Drupal we can properly use
-    // in that context, and it is overridable if necessary.
-    $element['#type'] = 'fieldset';
+    $element['#type'] = 'addressfield';
 
     // Generate the address form.
     $context = array(
@@ -478,6 +706,12 @@ function addressfield_field_widget_form(&$form, &$form_state, $field, $instance,
 
     // Mark the form element as required if necessary.
     $element['#required'] = $delta == 0 && $instance['required'];
+    $element['#non_empty_fields'] = $instance['settings']['non_empty_fields'];
+    $element['#non_empty_fields_op'] = $instance['settings']['non_empty_fields_op'];
+  }
+
+  if ($field['cardinality'] != 1) {
+    $element['#title'] = $instance['label'] . ' ' . ($delta + 1);
   }
 
   return $element;
