diff --git a/addressfield.module b/addressfield.module
index 177d92b..923e801 100644
--- a/addressfield.module
+++ b/addressfield.module
@@ -62,8 +62,8 @@ function addressfield_format_plugins_options() {
  * API for generating address forms and display formats.
  *
  * Addresses forms and display formats are collaboratively generated by one or
- * more format handler plugins. An address with a name and a company, for example,
- * will be generated by three handlers:
+ * more format handler plugins. An address with a name and a company, for
+ * example, will be generated by three handlers:
  *   - 'address' that will generate the country, locality, street blocks
  *   - 'organisation' that will add the organisation block to the address
  *   - 'name-full' that will add a first name and last name block to the address
@@ -81,12 +81,13 @@ function addressfield_format_plugins_options() {
  *     defined in '#widget_type' or using 'select' if '#options' is set or
  *     'textfield' if it is not. In addition, the '#default_value' of every
  *     field will be populated from the address being edited.
- *   - when rendering as a formatter, every element which name (its key in the array)
- *     is a valid addressfield column (see addressfield_field_schema()), will
- *     be transformed into a renderable element, either using a type explicitly
- *     defined in '#render_type' or else using 'addressfield_container'. When
- *     the type is 'addressfield_container' the element will be rendered as
- *     an HTML element set by '#tag' (default: span).
+ *   - when rendering as a formatter, every element which name (its key in the
+ *     array) is a valid addressfield column (see addressfield_field_schema()),
+ *     will be transformed into a renderable element, either using a type
+ *     explicitly defined in '#render_type' or else using
+ *     'addressfield_container'. When the type is 'addressfield_container' the
+ *     element will be rendered as an HTML element set by '#tag' (default:
+ *     span).
  */
 
 /**
@@ -127,7 +128,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'];
   }
   else {
     $format['#pre_render'][] = 'addressfield_render_address';
@@ -137,7 +140,8 @@ function addressfield_generate($address, array $handlers, array $context = array
 }
 
 /**
- * Generate a full-fledged form from a format snippet, as returned by addressfield_formats().
+ * Generates a full-fledged form from a format snippet, as returned by
+ * addressfield_formats().
  */
 function addressfield_process_format_form($format, &$form_state, $complete_form) {
   // Make sure to load all the plugins that participated in this format.
@@ -146,7 +150,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;
 }
 
@@ -170,7 +195,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']);
       }
 
@@ -267,9 +296,16 @@ function theme_addressfield_container($variables) {
 }
 
 /**
- * Implementation of hook_element_info().
+ * Implements 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'),
@@ -280,7 +316,7 @@ function addressfield_element_info() {
 }
 
 /**
- * Form API process function: set the #parents of the children of this element so they appear at the same level as the parent.
+ * Form API process function for processing addressfield_container elements.
  */
 function addressfield_widget_process($element) {
   foreach (element_children($element) as $key) {
@@ -292,7 +328,7 @@ function addressfield_widget_process($element) {
 }
 
 /**
- * Implements hook_field_info()
+ * Implements hook_field_info().
  */
 function addressfield_field_info() {
   $fields = array();
@@ -301,7 +337,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',
@@ -312,6 +351,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) {
@@ -320,8 +534,8 @@ function addressfield_default_values($available_countries = NULL) {
   }
   // Use the default country of the site if possible.
   $default_country = variable_get('site_default_country', NULL);
-  // If the default country is undefined or not in the list of available countries,
-  // just fallback to the first country in the list.
+  // If the default country is undefined or not in the list of available
+  // countries just fallback to the first country in the list.
   if (!$default_country || !isset($available_countries[$default_country])) {
     $default_country = key($available_countries);
   }
@@ -348,13 +562,32 @@ 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;
 }
 
 /**
- * Implements hook_field_widget_info()
+ * Implements hook_field_widget_info().
  */
 function addressfield_field_widget_info() {
   $widgets = array();
@@ -372,7 +605,7 @@ function addressfield_field_widget_info() {
 }
 
 /**
- * Implements hook_field_widget_settings_form()
+ * Implements hook_field_widget_settings_form().
  */
 function addressfield_field_widget_settings_form($field, $instance) {
   $widget = $instance['widget'];
@@ -401,7 +634,7 @@ function addressfield_field_widget_settings_form($field, $instance) {
 }
 
 /**
- * Implements hook_field_widget_form()
+ * Implements hook_field_widget_form().
  */
 function addressfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
   $settings = $instance['widget']['settings'];
@@ -444,10 +677,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(
@@ -461,13 +691,20 @@ 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;
 }
 
 /**
- * Element validate callback: rebuilds the form on country change and stores the current address value in the $form_state for retrieval on rebuild.
+ * Element validate callback for rebuilding the form on country change and
+ * stores the current address value in the $form_state for retrieval on rebuild.
  */
 function addressfield_standard_country_validate($element, &$form_state) {
   // If the country was changed, rebuild the form.
@@ -718,7 +955,8 @@ function _addressfield_country_options_list($field = NULL, $instance = NULL) {
   $countries = country_get_list();
 
   if (isset($field)) {
-    // If the instance is not specified, loop against all the instances of the field.
+    // If the instance is not specified, loop against all the instances of the
+    // field.
     if (!isset($instance)) {
       $instances = array();
       foreach ($field['bundles'] as $entity_type => $bundles) {
