diff --git a/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
new file mode 100644
index 0000000..cfdbc73
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\AllowedValuesInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for retrieving allowed values.
+ */
+interface AllowedValuesInterface {
+
+  /**
+   * Returns a list of all available values.
+   *
+   * @return array
+   *   The array of available values.
+   */
+  public function getAvailableValues(\Drupal\user\Plugin\Core\Entity\User $user = NULL);
+
+  /**
+   * Returns a list of all available values an their labels.
+   *
+   * This is similar to hook_options_list().
+   * @see hook_options_list()
+   *
+   * @return array
+   *   The array of  valid values. Array keys are the values to be stored, and
+   *   should be of the data type (string, number...) expected by the first
+   *   'column' for the field type. Array values are the labels to display
+   *   within the option list.
+   */
+  public function getAvailableOptions(\Drupal\user\Plugin\Core\Entity\User $user = NULL);
+
+  /**
+   * Returns a list of the allowed values in the current context.
+   *
+   * @param \Drupal\user\Plugin\Core\Entity\User $user
+   *   The user to use as context.
+   *
+   * @return array
+   *   The array of allowed values.
+   */
+  public function getAllowedValues(\Drupal\user\Plugin\Core\Entity\User $user = NULL);
+
+  /**
+   * Returns a list of valid values an their labels.
+   *
+   * This is similar to hook_options_list().
+   * @see hook_options_list()
+   *
+   * @param \Drupal\user\Plugin\Core\Entity\User $user
+   *   The user to use as context.
+   *
+   * @return array
+   *   The array of  valid values. Array keys are the values to be stored, and
+   *   should be of the data type (string, number...) expected by the first
+   *   'column' for the field type. Array values are the labels to display
+   *   within the option list.
+   */
+  public function getAllowedOptions(\Drupal\user\Plugin\Core\Entity\User $user = NULL);
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 195e32d..6dd17a1 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -389,6 +389,17 @@ public function getConstraints($definition) {
     if (!empty($definition['required']) && empty($definition['constraints']['NotNull'])) {
       $constraints[] = $this->createValidationConstraint('NotNull', array());
     }
+
+    // If the definition provides a class check for further validation criteria.
+    $class = isset($definition['class']) ? $definition['class'] : $type_definition['class'];
+    if (isset($class)) {
+      $implements = class_implements($class);
+      // Check if the class provides allowed values.
+      if (isset($implements['Drupal\Core\TypedData\AllowedValuesInterface'])) {
+        $constraints[] = $this->createValidationConstraint('AllowedValues', array());
+      }
+    }
+
     return $constraints;
   }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
index 106f2d0..83ca0de 100644
--- a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
@@ -91,4 +91,14 @@ public function getPropertyName() {
   public function getPropertyValue($container) {
     return $this->typedData->getValue();
   }
+
+  /**
+   * Returns the typed data object.
+   *
+   * @return TypedDataInterface
+   *   The typed data object.
+   */
+  public function getTypedData() {
+    return $this->typedData;
+  }
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php
new file mode 100644
index 0000000..1c5c067
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Constraint\AllowedValuesConstraint.
+ */
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraints\Choice;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+
+/**
+ * Checks if a value is a valid entity type.
+ *
+ * @Plugin(
+ *   id = "AllowedValues",
+ *   label = @Translation("AllowedValues", context = "Validation"),
+ * )
+ */
+class AllowedValuesConstraint extends Choice {
+
+  /**
+   * Overrides Constraint::getDefaultOption().
+   */
+  public function getDefaultOption() {
+    return '';
+  }
+
+  /**
+   * Overrides Constraint::getRequiredOptions().
+   */
+  public function getRequiredOptions() {
+    return array();
+  }
+
+  /**
+   * Set allowed values.
+   *
+   * @param array $allowed_values
+   *   The list of allowed values.
+   */
+  public function setAllowedValues(array $allowed_values) {
+    $this->choices = $allowed_values;
+  }
+}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
new file mode 100644
index 0000000..f0afd04
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraintValidator.
+ */
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Drupal\Core\TypedData\AllowedValuesInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\ChoiceValidator;
+
+/**
+ * Validates the EntityType constraint.
+ */
+class AllowedValuesConstraintValidator extends ChoiceValidator {
+
+  /**
+   * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
+   */
+  public function validate($typed_data, Constraint $constraint) {
+    if ($this->context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) {
+      $allowed_values = $this->context->getMetadata()->getTypedData()->getAllowedValues();
+      $constraint->setAllowedValues($allowed_values);
+      return parent::validate($typed_data, $constraint);
+    }
+  }
+}
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 06d3fff..b2c6eaf 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -8,6 +8,7 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+use Drupal\Core\TypedData\Primitive;
 
 /**
  * Non-HTML markup language filters that generate HTML.
@@ -110,6 +111,19 @@ function filter_element_info() {
 }
 
 /**
+ * Implements hook_data_type_info().
+ */
+function filter_data_type_info() {
+  return array(
+    'filter_format' => array(
+      'label' => t('Filter format'),
+      'class' => 'Drupal\filter\Type\FilterFormat',
+      'primitive type' => Primitive::STRING,
+    ),
+  );
+}
+
+/**
  * Implements hook_menu().
  */
 function filter_menu() {
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
index 3918ea9..841e75a 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
@@ -106,4 +106,89 @@ function testFilterFormatAPI() {
     );
   }
 
+  /**
+   * Tests the function of the typed data type.
+   */
+  function testTypedDataAPI() {
+    $definition = array('type' => 'filter_format');
+    $data = typed_data()->create($definition);
+
+    if ($this->assertTrue($data instanceof \Drupal\Core\TypedData\AllowedValuesInterface, 'Typed data object implements \Drupal\Core\TypedData\AllowedValuesInterface')) {
+      $this->checkPermissions(array(), TRUE);
+      $filtered_html_user = $this->drupalCreateUser(array(
+        filter_permission_name(filter_format_load('filtered_html')),
+      ));
+      // @todo Is there really no nicer way to do this?
+      $global_user = $GLOBALS['user'];
+      $GLOBALS['user'] = drupal_anonymous_user();
+
+      $available_values = $data->getAvailableValues();
+      $this->assertEqual(
+        $available_values,
+        array('filtered_html', 'full_html', 'plain_text'),
+        'Expected available format values found'
+      );
+      $available_options = $data->getAvailableOptions();
+      $this->assertEqual(
+        $available_options,
+        array(
+          'filtered_html' => 'Filtered HTML',
+          'full_html' => 'Full HTML',
+          'plain_text' => 'Plain text'
+        ),
+        'Expected available format options found'
+      );
+      $allowed_values = $data->getAllowedValues();
+      $this->assertEqual(
+        $allowed_values,
+        array('plain_text'),
+        'Expected allowed format values for anoymous found'
+      );
+      $allowed_options = $data->getAllowedOptions();
+      $this->assertEqual(
+        $allowed_options,
+        array(
+          'plain_text' => 'Plain text'
+        ),
+        'Expected allowed format options for anoymous found'
+      );
+
+      $data->setValue('foo');
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 1, "Validation violation about for format 'foo' found");
+
+      $data->setValue('plain_text');
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 0, "No validation violation for format 'plain_text' found");
+
+      // Anonymous doesn't have access to the 'filtered_html' format.
+      $data->setValue('filtered_html');
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 1, "Validation violation for protected format 'filtered_html' found.");
+
+      // Define user with access to 'filtered_html' format.
+      $GLOBALS['user'] = $filtered_html_user;
+      $violations = $data->validate();
+      $this->assertEqual(count($violations), 0, "No validation violation for accessible format 'filtered_html' found.");
+
+      $allowed_values = $data->getAllowedValues();
+      $this->assertEqual(
+        $allowed_values,
+        array('filtered_html', 'plain_text'),
+        'Expected allowed format values for authenticated user found'
+      );
+      $allowed_options = $data->getAllowedOptions();
+      $this->assertEqual(
+        $allowed_options,
+        array(
+          'filtered_html' => 'Filtered HTML',
+          'plain_text' => 'Plain text'
+        ),
+        'Expected allowed format options for authenticated user found'
+      );
+
+      $GLOBALS['user'] = $global_user;
+    }
+  }
+
 }
diff --git a/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php
new file mode 100644
index 0000000..c66f58a
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filter\Type\FilterFormat.
+ */
+
+namespace Drupal\filter\Type;
+
+use Drupal\Core\TypedData\Type\String;
+use Drupal\Core\TypedData\AllowedValuesInterface;
+
+/**
+ * The filter format data type.
+ */
+class FilterFormat extends String implements AllowedValuesInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableValues(\Drupal\user\Plugin\Core\Entity\User $user = NULL) {
+    return array_keys($this->getAvailableOptions());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAvailableOptions(\Drupal\user\Plugin\Core\Entity\User $user = NULL) {
+    $values = array();
+    // @todo Evil call to procedural code, how can we get rid of it?
+    foreach (filter_formats() as $format) {
+      $values[$format->id()] = $format->label();
+    }
+    return $values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllowedValues(\Drupal\user\Plugin\Core\Entity\User $user = NULL) {
+    return array_keys($this->getAllowedOptions($user));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllowedOptions(\Drupal\user\Plugin\Core\Entity\User $user = NULL) {
+    $user = empty($user) ? $GLOBALS['user'] : $user;
+    $values = array();
+    // @todo Evil call to procedural code, how can we get rid of it?
+    foreach (filter_formats($user) as $format) {
+      $values[$format->id()] = $format->label();
+    }
+    return $values;
+  }
+}
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextFormattedUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextFormattedUnitTest.php
new file mode 100644
index 0000000..5b6b50a
--- /dev/null
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextFormattedUnitTest.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\text\Tests\Formatter\TextPlainUnitTest.
+ */
+
+namespace Drupal\text\Tests\Formatter;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests the text_plain field formatter.
+ *
+ * @todo Move assertion helper methods into DrupalUnitTestBase.
+ * @todo Move field helper methods, $modules, and setUp() into a new
+ *   FieldPluginUnitTestBase.
+ */
+class TextFormattedUnitTest extends TextPlainUnitTest {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('system', 'entity', 'field', 'field_sql_storage', 'text', 'field_test', 'user', 'filter', 'entity_test');
+
+  /**
+   * Contains rendered content.
+   *
+   * @var string
+   */
+  protected $content;
+
+  public static function getInfo() {
+    return array(
+      'name'  => 'Text field formatted text',
+      'description'  => "Test the creation of text fields with a filter format.",
+      'group' => 'Field types',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    // Configure the theme system.
+    $this->installConfig(array('system', 'field', 'filter'));
+
+    // @todo Add helper methods for all of the following.
+
+    $this->entity_type = 'entity_test';
+    if (!isset($this->bundle)) {
+      $this->bundle = $this->entity_type;
+    }
+
+    $this->field_name = drupal_strtolower($this->randomName());
+    $this->field_type = 'text_long';
+    $this->field_settings = array();
+    $this->instance_settings = array(
+      'text_processing' => TRUE,
+    );
+
+    $this->formatter_type = 'text_plain';
+    $this->formatter_settings = array();
+
+    $this->field = array(
+      'field_name' => $this->field_name,
+      'type' => $this->field_type,
+      'settings' => $this->field_settings,
+    );
+    $this->field = field_create_field($this->field);
+
+    $this->instance = array(
+      'entity_type' => $this->entity_type,
+      'bundle' => $this->bundle,
+      'field_name' => $this->field_name,
+      'label' => $this->randomName(),
+      'settings' => $this->instance_settings,
+    );
+    $this->instance = field_create_instance($this->instance);
+
+    $this->view_mode = 'default';
+    $this->display = entity_get_display($this->entity_type, $this->bundle, $this->view_mode)
+      ->setComponent($this->field_name, array(
+        'type' => $this->formatter_type,
+        'settings' => $this->formatter_settings,
+      ));
+    $this->display->save();
+
+    $this->langcode = LANGUAGE_NOT_SPECIFIED;
+  }
+
+  /**
+   * Tests text_plain formatter output.
+   */
+  function testPlainText() {
+    $value = $this->randomString();
+
+    $entity = $this->createEntity(array());
+    $this->setFieldItem($entity, $this->field_name, array(
+      'value' => $value,
+      'format' => 'x',
+    ));
+    $x = $entity->{$this->field_name}->validate();
+    debug($x);
+  }
+
+}
diff --git a/core/modules/text/lib/Drupal/text/Type/TextItem.php b/core/modules/text/lib/Drupal/text/Type/TextItem.php
index b71812f..9e5e7a6 100644
--- a/core/modules/text/lib/Drupal/text/Type/TextItem.php
+++ b/core/modules/text/lib/Drupal/text/Type/TextItem.php
@@ -34,7 +34,7 @@ public function getPropertyDefinitions() {
         'label' => t('Text value'),
       );
       static::$propertyDefinitions['format'] = array(
-        'type' => 'string',
+        'type' => 'filter_format',
         'label' => t('Text format'),
       );
       static::$propertyDefinitions['processed'] = array(
