diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 82e5241..8985f31 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Entity\Plugin\DataType\EntityReference;
+use Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper;
 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Session\AccountInterface;
@@ -220,86 +221,9 @@ public function getDataDefinition() {
   /**
    * {@inheritdoc}
    */
-  public function getValue() {
-    // @todo: This does not make much sense, so remove once TypedDataInterface
-    // is removed. See https://drupal.org/node/2002138.
-    return $this->getPropertyValues();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setValue($value, $notify = TRUE) {
-    // @todo: This does not make much sense, so remove once TypedDataInterface
-    // is removed. See https://drupal.org/node/2002138.
-    $this->setPropertyValues($value);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getString() {
-    return $this->label();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function validate() {
     // @todo: Add the typed data manager as proper dependency.
-    return \Drupal::typedDataManager()->getValidator()->validate($this);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function applyDefaultValue($notify = TRUE) {
-    foreach ($this->getProperties() as $property) {
-      $property->applyDefaultValue(FALSE);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConstraints() {
-    return array();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getName() {
-    return NULL;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getRoot() {
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPropertyPath() {
-    return '';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getParent() {
-    return NULL;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setContext($name = NULL, TypedDataInterface $parent = NULL) {
-    // As entities are always the root of the tree of typed data, we do not need
-    // to set any parent or name.
+    return \Drupal::typedDataManager()->getValidator()->validate(new EntityTypedDataWrapper($this));
   }
 
   /**
@@ -378,11 +302,11 @@ public function hasField($field_name) {
   /**
    * {@inheritdoc}
    */
-  public function get($property_name) {
-    if (!isset($this->fields[$property_name][$this->activeLangcode])) {
-      return $this->getTranslatedField($property_name, $this->activeLangcode);
+  public function get($field_name) {
+    if (!isset($this->fields[$field_name][$this->activeLangcode])) {
+      return $this->getTranslatedField($field_name, $this->activeLangcode);
     }
-    return $this->fields[$property_name][$this->activeLangcode];
+    return $this->fields[$field_name][$this->activeLangcode];
   }
 
   /**
@@ -417,7 +341,8 @@ protected function getTranslatedField($name, $langcode) {
         if (isset($this->values[$name][$langcode])) {
           $value = $this->values[$name][$langcode];
         }
-        $field = \Drupal::typedDataManager()->getPropertyInstance($this, $name, $value);
+        $wrapper = new EntityTypedDataWrapper($this);
+        $field = \Drupal::typedDataManager()->getPropertyInstance($wrapper, $name, $value);
         if ($default) {
           // $this->defaultLangcode might not be set if we are initializing the
           // default language code cache, in which case there is no valid
@@ -446,21 +371,21 @@ public function set($name, $value, $notify = TRUE) {
   /**
    * {@inheritdoc}
    */
-  public function getProperties($include_computed = FALSE) {
-    $properties = array();
+  public function getFields($include_computed = FALSE) {
+    $fields = array();
     foreach ($this->getFieldDefinitions() as $name => $definition) {
       if ($include_computed || !$definition->isComputed()) {
-        $properties[$name] = $this->get($name);
+        $fields[$name] = $this->get($name);
       }
     }
-    return $properties;
+    return $fields;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getIterator() {
-    return new \ArrayIterator($this->getProperties());
+    return new \ArrayIterator($this->getFields());
   }
 
   /**
@@ -491,9 +416,9 @@ public function getFieldDefinitions() {
   /**
    * {@inheritdoc}
    */
-  public function getPropertyValues() {
+  public function toArray() {
     $values = array();
-    foreach ($this->getProperties() as $name => $property) {
+    foreach ($this->getFields() as $name => $property) {
       $values[$name] = $property->getValue();
     }
     return $values;
@@ -502,30 +427,6 @@ public function getPropertyValues() {
   /**
    * {@inheritdoc}
    */
-  public function setPropertyValues($values) {
-    foreach ($values as $name => $value) {
-      $this->get($name)->setValue($value);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isEmpty() {
-    if (!$this->isNew()) {
-      return FALSE;
-    }
-    foreach ($this->getProperties() as $property) {
-      if ($property->getValue() !== NULL) {
-        return FALSE;
-      }
-    }
-    return TRUE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function access($operation = 'view', AccountInterface $account = NULL) {
     if ($operation == 'create') {
       return \Drupal::entityManager()
@@ -603,8 +504,6 @@ public function onChange($name) {
 
   /**
    * {@inheritdoc}
-   *
-   * @return \Drupal\Core\Entity\ContentEntityInterface
    */
   public function getTranslation($langcode) {
     // Ensure we always use the default language code when dealing with the
@@ -782,15 +681,6 @@ public function getTranslationLanguages($include_default = TRUE) {
   }
 
   /**
-   * Overrides Entity::translations().
-   *
-   * @todo: Remove once Entity::translations() gets removed.
-   */
-  public function translations() {
-    return $this->getTranslationLanguages(FALSE);
-  }
-
-  /**
    * Updates the original values with the interim changes.
    */
   public function updateOriginalValues() {
@@ -935,7 +825,7 @@ public function __clone() {
         }
         foreach ($values as $langcode => $items) {
           $this->fields[$name][$langcode] = clone $items;
-          $this->fields[$name][$langcode]->setContext($name, $this);
+          $this->fields[$name][$langcode]->setContext($name, new EntityTypedDataWrapper($this));
         }
       }
 
@@ -970,7 +860,7 @@ public function referencedEntities() {
     $referenced_entities = array();
 
     // Gather a list of referenced entities.
-    foreach ($this->getProperties() as $field_items) {
+    foreach ($this->getFields() as $field_items) {
       foreach ($field_items as $field_item) {
         // Loop over all properties of a field item.
         foreach ($field_item->getProperties(TRUE) as $property) {
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
index dee43a0..079e5f6 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -26,7 +26,7 @@
  * @see \Drupal\Core\TypedData\TypedDataManager
  * @see \Drupal\Core\Field\FieldItemListInterface
  */
-interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface, ComplexDataInterface {
+interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface {
 
   /**
    * Marks the translation identified by the given language code as existing.
@@ -124,4 +124,69 @@ public function getFieldDefinition($name);
    */
   public function getFieldDefinitions();
 
+  /**
+   * Gets a field item list.
+   *
+   * @param $field_name
+   *   The name of the field to get; e.g., 'title' or 'name'.
+   *
+   * @throws \InvalidArgumentException
+   *   If an invalid field name is given.
+   *
+   * @return \Drupal\Core\Field\FieldItemListInterface
+   *   The field item list, containing the field items.
+   */
+  public function get($field_name);
+
+  /**
+   * Sets a field value.
+   *
+   * @param $field_name
+   *   The name of the property to set; e.g., 'title' or 'name'.
+   * @param $value
+   *   The value to set, or NULL to unset the field.
+   * @param bool $notify
+   *   (optional) Whether to notify the entity of the change. Defaults to
+   *   TRUE. If the update stems from the entity, set it to FALSE to avoid
+   *   being notified again.
+   *
+   * @throws \InvalidArgumentException
+   *   If the specified field does not exist.
+   *
+   * @return $this
+   */
+  public function set($field_name, $value, $notify = TRUE);
+
+  /**
+   * Gets an array of field item lists.
+   *
+   * @param bool $include_computed
+   *   If set to TRUE, computed fields are included. Defaults to FALSE.
+   *
+   * @return \Drupal\Core\Field\FieldItemListInterface[]
+   *   An array of field item lists implementing, keyed by field name.
+   */
+  public function getFields($include_computed = FALSE);
+
+  /**
+   * Gets an array of field values.
+   *
+   * Gets an array of plain field values including all non-computed
+   * properties.
+   *
+   * @return array
+   *   An array keyed by property name containing the property value.
+   */
+  public function toArray();
+
+  /**
+   * React to changes to a child field.
+   *
+   * Note that this is invoked after any changes have been applied.
+   *
+   * @param $field_name
+   *   The name of the field which is changed.
+   */
+  public function onChange($field_name);
+
 }
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
index 5d01819..819a255 100644
--- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper;
 use Drupal\Core\Field\PrepareCacheInterface;
 use Drupal\field\FieldConfigInterface;
 use Drupal\field\FieldInstanceConfigInterface;
@@ -341,7 +342,7 @@ public function onBundleDelete($bundle) { }
    */
   public function onFieldItemsPurge(EntityInterface $entity, FieldInstanceConfigInterface $instance) {
     if ($values = $this->readFieldItemsToPurge($entity, $instance)) {
-      $items = \Drupal::typedDataManager()->create($instance, $values, $instance->getName(), $entity);
+      $items = \Drupal::typedDataManager()->create($instance, $values, $instance->getName(), new EntityTypedDataWrapper($entity));
       $items->delete();
     }
     $this->purgeFieldItems($entity, $instance);
@@ -412,7 +413,7 @@ protected function invokeTranslationHooks(ContentEntityInterface $entity) {
   protected function invokeFieldMethod($method, ContentEntityInterface $entity) {
     foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
       $translation = $entity->getTranslation($langcode);
-      foreach ($translation->getProperties(TRUE) as $field) {
+      foreach ($translation->getFields(TRUE) as $field) {
         $field->$method();
       }
     }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php
deleted file mode 100644
index 785cde9..0000000
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Plugin\DataType\Entity.
- */
-
-namespace Drupal\Core\Entity\Plugin\DataType;
-
-/**
- * Defines the base plugin for deriving data types for entity types.
- *
- * Note that the class only registers the plugin, and is actually never used.
- * \Drupal\Core\Entity\Entity is available for use as base class.
- *
- * @DataType(
- *   id = "entity",
- *   label = @Translation("Entity"),
- *   description = @Translation("All kind of entities, e.g. nodes, comments or users."),
- *   derivative = "\Drupal\Core\Entity\Plugin\DataType\Deriver\EntityDeriver",
- *   definition_class = "\Drupal\Core\Entity\TypedData\EntityDataDefinition"
- * )
- */
-abstract class Entity {
-
-}
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityTypedDataWrapper.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityTypedDataWrapper.php
new file mode 100644
index 0000000..3cc85fd
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityTypedDataWrapper.php
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper.
+ */
+
+namespace Drupal\Core\Entity\Plugin\DataType;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\DataDefinitionInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+
+/**
+ * Defines the base plugin for deriving data types for entity types.
+ *
+ * Note that the class only registers the plugin, and is actually never used.
+ * \Drupal\Core\Entity\Entity is available for use as base class.
+ *
+ * @DataType(
+ *   id = "entity",
+ *   label = @Translation("Entity"),
+ *   description = @Translation("All kind of entities, e.g. nodes, comments or users."),
+ *   derivative = "\Drupal\Core\Entity\Plugin\DataType\Deriver\EntityDeriver",
+ *   definition_class = "\Drupal\Core\Entity\TypedData\EntityDataDefinition"
+ * )
+ */
+class EntityTypedDataWrapper implements \IteratorAggregate, ComplexDataInterface {
+
+  /**
+   * @var \Drupal\Core\Entity\ContentEntityInterface
+   */
+  protected $entity;
+
+  public function __construct(ContentEntityInterface $entity) {
+    if ($entity instanceof DataDefinitionInterface) {
+      debug(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
+    }
+    $this->entity = $entity;
+  }
+
+  public function getEntity() {
+    return $this->entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($property_name) {
+    $this->entity->get($property_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($property_name, $value, $notify = TRUE) {
+    return $this->entity->set($property_name, $value, $notify);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProperties($include_computed = FALSE) {
+    return $this->entity->getFields($include_computed);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyValues() {
+    return $this->entity->toArray();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setPropertyValues($values) {
+    // TODO: Implement setPropertyValues() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    // TODO: Implement isEmpty() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($property_name) {
+    $this->entity->onChange($property_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDataDefinition() {
+    return $this->entity->getDataDefinition();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue() {
+    // TODO: Implement getValue() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    // TODO: Implement setValue() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getString() {
+    // TODO: Implement getString() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstraints() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate() {
+    return $this->entity->validate();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applyDefaultValue($notify = TRUE) {
+    // TODO: Implement applyDefaultValue() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    // TODO: Implement getName() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getParent() {
+    // TODO: Implement getParent() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoot() {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyPath() {
+    // TODO: Implement getPropertyPath() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setContext($name = NULL, TypedDataInterface $parent = NULL) {
+    // TODO: Implement setContext() method.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIterator() {
+    return $this->entity->getIterator();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Field/FieldItemInterface.php b/core/lib/Drupal/Core/Field/FieldItemInterface.php
index ae7bf6c..31d403c 100644
--- a/core/lib/Drupal/Core/Field/FieldItemInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemInterface.php
@@ -86,7 +86,7 @@ public static function schema(FieldDefinitionInterface $field_definition);
   /**
    * Gets the entity that field belongs to.
    *
-   * @return \Drupal\Core\Entity\EntityInterface
+   * @return \Drupal\Core\Entity\ContentEntityInterface
    *   The entity object.
    */
   public function getEntity();
diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index a3e64b0..21a9ab9 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Field;
 
+use Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
@@ -54,7 +55,7 @@ public function __construct(DataDefinitionInterface $definition, $name = NULL, T
    * {@inheritdoc}
    */
   public function getEntity() {
-    return $this->getParent();
+    return $this->getParent() instanceof EntityTypedDataWrapper ? $this->getParent()->getEntity() : $this->getParent();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
index af66bfe..fa75f4f 100644
--- a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
@@ -45,7 +45,7 @@ public function getTranslationLanguages($include_default = TRUE);
    *   The language code of the translation to get or Language::LANGCODE_DEFAULT
    *   to get the data in default language.
    *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
+   * @return $this
    *   A typed data object for the translated data.
    */
   public function getTranslation($langcode);
@@ -53,7 +53,7 @@ public function getTranslation($langcode);
   /**
    * Returns the translatable object referring to the original language.
    *
-   * @return \Drupal\Core\TypedData\TranslatableInterface
+   * @return $this
    *   The translation object referring to the original language.
    */
   public function getUntranslated();
@@ -78,7 +78,7 @@ public function hasTranslation($langcode);
    *   (optional) An array of initial values to be assigned to the translatable
    *   fields. Defaults to none.
    *
-   * @return \Drupal\Core\TypedData\TranslatableInterface
+   * @return $this
    */
   public function addTranslation($langcode, array $values = array());
 
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFieldNameValue.php b/core/modules/comment/lib/Drupal/comment/CommentFieldNameValue.php
index 0190b8d..23c4d99 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFieldNameValue.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFieldNameValue.php
@@ -24,8 +24,7 @@ public function getValue() {
       if (!isset($this->parent)) {
         throw new InvalidArgumentException('Computed properties require context for computation.');
       }
-      $field = $this->parent->getParent();
-      $entity = $field->getParent();
+      $entity = $this->parent->getEntity();
       // Field id is of the form {entity_type}__{field_name}. We set the
       // optional limit param to explode() in case the user adds a field with __
       // in the name.
@@ -44,8 +43,7 @@ public function setValue($value, $notify = TRUE) {
     if (isset($value)) {
       $this->field_name = $value;
       // Also set the field id.
-      $field = $this->parent->getParent();
-      $entity = $field->getParent();
+      $entity = $this->parent->getEntity();
       $entity->field_id = $entity->getCommentedEntityTypeId() . '__' . $value;
     }
   }
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldWidget/CommentWidget.php b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldWidget/CommentWidget.php
index b4a8565..628ec43 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldWidget/CommentWidget.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Field/FieldWidget/CommentWidget.php
@@ -29,7 +29,7 @@ class CommentWidget extends WidgetBase {
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
     $field = $this->fieldDefinition;
-    $entity = $items->getParent();
+    $entity = $items->getEntity();
 
     // Get default value from the field instance.
     $field_default_values = $this->fieldDefinition->getDefaultValue($entity);
diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc
index ac36ff7..44c82d0 100644
--- a/core/modules/content_translation/content_translation.pages.inc
+++ b/core/modules/content_translation/content_translation.pages.inc
@@ -229,6 +229,6 @@ function content_translation_edit_page(EntityInterface $entity, Language $langua
 function content_translation_prepare_translation(EntityInterface $entity, Language $source, Language $target) {
   if ($entity instanceof ContentEntityInterface) {
     $source_translation = $entity->getTranslation($source->id);
-    $entity->addTranslation($target->id, $source_translation->getPropertyValues());
+    $entity->addTranslation($target->id, $source_translation->toArray());
   }
 }
diff --git a/core/modules/hal/hal.services.yml b/core/modules/hal/hal.services.yml
index f23f8b8..8a28cc8 100644
--- a/core/modules/hal/hal.services.yml
+++ b/core/modules/hal/hal.services.yml
@@ -18,7 +18,7 @@ services:
       - { name: normalizer, priority: 20 }
     arguments: ['@entity.manager', '@http_default_client', '@rest.link_manager']
   serializer.normalizer.entity.hal:
-    class: Drupal\hal\Normalizer\EntityNormalizer
+    class: Drupal\hal\Normalizer\ContentEntityNormalizer
     arguments: ['@rest.link_manager']
     tags:
       - { name: normalizer, priority: 10 }
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/ContentEntityNormalizer.php
similarity index 90%
rename from core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php
rename to core/modules/hal/lib/Drupal/hal/Normalizer/ContentEntityNormalizer.php
index 8fbcffc..8a1cfd0 100644
--- a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/ContentEntityNormalizer.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\hal\Normalizer\EntityNormalizer.
+ * Contains \Drupal\hal\Normalizer\ContentEntityNormalizer.
  */
 
 namespace Drupal\hal\Normalizer;
@@ -15,14 +15,14 @@
 /**
  * Converts the Drupal entity object structure to a HAL array structure.
  */
-class EntityNormalizer extends NormalizerBase {
+class ContentEntityNormalizer extends NormalizerBase {
 
   /**
    * The interface or class that this Normalizer supports.
    *
    * @var string
    */
-  protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\EntityInterface';
+  protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\ContentEntityInterface';
 
   /**
    * The hypermedia link manager.
@@ -32,7 +32,7 @@ class EntityNormalizer extends NormalizerBase {
   protected $linkManager;
 
   /**
-   * Constructs an EntityNormalizer object.
+   * Constructs an ContentEntityNormalizer object.
    *
    * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager
    *   The hypermedia link manager.
@@ -61,21 +61,20 @@ public function normalize($entity, $format = NULL, array $context = array()) {
     // If the properties to use were specified, only output those properties.
     // Otherwise, output all properties except internal ID.
     if (isset($context['included_fields'])) {
-      $properties = array();
-      foreach ($context['included_fields'] as $property_name) {
-        $properties[] = $entity->get($property_name);
+      $fields = array();
+      foreach ($context['included_fields'] as $field_name) {
+        $fields[] = $entity->get($field_name);
       }
     }
     else {
-      $properties = $entity->getProperties();
+      $fields = $entity->getFields();
     }
-    foreach ($properties as $property) {
-      // In some cases, Entity API will return NULL array items. Ensure this is
-      // a real property and that it is not the internal id.
-      if (!is_object($property) || $property->getName() == 'id') {
+    foreach ($fields as $field) {
+      // Ignore the entity ID.
+      if ($field->getFieldDefinition()->getName() == $entity->getEntityType()->getKey('id')) {
         continue;
       }
-      $normalized_property = $this->serializer->normalize($property, $format, $context);
+      $normalized_property = $this->serializer->normalize($field, $format, $context);
       $normalized = NestedArray::mergeDeep($normalized, $normalized_property);
     }
 
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
index 1ffc72f..06e0ad8 100644
--- a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityReferenceItemNormalizer.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\hal\Normalizer;
 
+use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\rest\LinkManager\LinkManagerInterface;
 use Drupal\serialization\EntityResolver\EntityResolverInterface;
 use Drupal\serialization\EntityResolver\UuidReferenceInterface;
@@ -66,6 +67,10 @@ public function normalize($field_item, $format = NULL, array $context = array())
 
     // Normalize the target entity.
     $embedded = $this->serializer->normalize($target_entity, $format, $context);
+    // @todo Config entities currently can not be serialized, skip them.
+    if (empty($embedded['_links']['self'])) {
+      return array();
+    }
     $link = $embedded['_links']['self'];
     // If the field is translatable, add the langcode to the link relation
     // object. This does not indicate the language of the target entity.
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
index 44e6787..2a14f9c 100644
--- a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldItemNormalizer.php
@@ -99,28 +99,19 @@ protected function constructValue($data, $context) {
    *   The translated field item instance.
    */
   protected function createTranslatedInstance(FieldItemInterface $field_item, $langcode) {
-    $parent = $field_item->getParent();
-    $ancestors = array();
+    $field_items = $field_item->getParent();
 
     // Remove the untranslated instance from the field's list of items.
-    $parent->offsetUnset($field_item->getName());
+    $field_items->offsetUnset($field_item->getName());
 
-    // Get the property path.
-    while (!method_exists($parent, 'getTranslation')) {
-      array_unshift($ancestors, $parent);
-      $parent = $parent->getParent();
-    }
+    // Get the entity in the requested language and the field's item list from
+    // that.
+    $entity_translation = $field_item->getEntity()->getTranslation($langcode);
+    $field_items_translation = $entity_translation->get($field_item->getFieldDefinition()->getName());
 
-    // Recreate the property path with translations.
-    $translation = $parent->getTranslation($langcode);
-    foreach ($ancestors as $ancestor) {
-      $ancestor_name =  $ancestor->getName();
-      $translation = $translation->get($ancestor_name);
-    }
-
-    // Create a new instance at the end of the property path and return it.
-    $count = $translation->isEmpty() ? 0 : $translation->count();
-    return $translation->get($count);
+    // Create a new instance and return it.
+    $count = $field_items_translation->isEmpty() ? 0 : $field_items_translation->count();
+    return $field_items_translation->get($count);
   }
 
 }
diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php
index 815ed6e..e8d0bc6 100644
--- a/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php
+++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php
@@ -15,7 +15,7 @@
 /**
  * Converts the Drupal entity object structure to a HAL array structure.
  */
-class FileEntityNormalizer extends EntityNormalizer {
+class FileEntityNormalizer extends ContentEntityNormalizer {
 
   /**
    * The interface or class that this Normalizer supports.
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
index 99e8de7..aa2473d 100644
--- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
@@ -10,7 +10,7 @@
 use Drupal\Core\Cache\MemoryBackend;
 use Drupal\Core\Language\Language;
 use Drupal\hal\Encoder\JsonEncoder;
-use Drupal\hal\Normalizer\EntityNormalizer;
+use Drupal\hal\Normalizer\ContentEntityNormalizer;
 use Drupal\hal\Normalizer\EntityReferenceItemNormalizer;
 use Drupal\hal\Normalizer\FieldItemNormalizer;
 use Drupal\hal\Normalizer\FieldNormalizer;
@@ -124,7 +124,7 @@ function setUp() {
 
     // Set up the mock serializer.
     $normalizers = array(
-      new EntityNormalizer($link_manager),
+      new ContentEntityNormalizer($link_manager),
       new EntityReferenceItemNormalizer($link_manager, new UuidResolver()),
       new FieldItemNormalizer(),
       new FieldNormalizer(),
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Condition/NodeType.php b/core/modules/node/lib/Drupal/node/Plugin/Condition/NodeType.php
index 3739209..3a499c8 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/Condition/NodeType.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Condition/NodeType.php
@@ -84,7 +84,7 @@ public function summary() {
    */
   public function evaluate() {
     $node = $this->getContextValue('node');
-    return in_array($node->getType(), $this->configuration['bundles']);
+    return in_array($node->getEntity()->getType(), $this->configuration['bundles']);
   }
 
 }
diff --git a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php
index 065cdca..34f4c3c 100644
--- a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\node\Tests\Condition;
 
+use Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper;
 use Drupal\system\Tests\Entity\EntityUnitTestBase;
 
 /**
@@ -56,7 +57,7 @@ function testConditions() {
     // of 'article' and set the context to the page type node.
     $condition = $manager->createInstance('node_type')
       ->setConfig('bundles', array('article'))
-      ->setContextValue('node', $page);
+      ->setContextValue('node', new EntityTypedDataWrapper($page));
     $this->assertFalse($condition->execute(), 'Page type nodes fail node type checks for articles.');
     // Check for the proper summary.
     $this->assertEqual('The node bundle is article', $condition->summary());
@@ -74,11 +75,11 @@ function testConditions() {
     $this->assertEqual('The node bundle is page or article', $condition->summary());
 
     // Set the context to the article node.
-    $condition->setContextValue('node', $article);
+    $condition->setContextValue('node', new EntityTypedDataWrapper($article));
     $this->assertTrue($condition->execute(), 'Article type nodes pass node type checks for pages or articles');
 
     // Set the context to the test node.
-    $condition->setContextValue('node', $test);
+    $condition->setContextValue('node', new EntityTypedDataWrapper($test));
     $this->assertFalse($condition->execute(), 'Test type nodes pass node type checks for pages or articles');
 
     // Check a greater than 2 bundles summary scenario.
@@ -86,7 +87,7 @@ function testConditions() {
     $this->assertEqual('The node bundle is page, article or test', $condition->summary());
 
     // Test Constructor injection.
-    $condition = $manager->createInstance('node_type', array('bundles' => array('article'), 'context' => array('node' => $article)));
+    $condition = $manager->createInstance('node_type', array('bundles' => array('article'), 'context' => array('node' => new EntityTypedDataWrapper($article))));
     $this->assertTrue($condition->execute(), 'Constructor injection of context and configuration working as anticipated.');
   }
 }
diff --git a/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/EntityNormalizerTest.php b/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/EntityNormalizerTest.php
index f162798..27d6e63 100644
--- a/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/EntityNormalizerTest.php
+++ b/core/modules/serialization/tests/Drupal/serialization/Tests/Normalizer/EntityNormalizerTest.php
@@ -73,10 +73,10 @@ public function testNormalize() {
 
     $content_entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
       ->disableOriginalConstructor()
-      ->setMethods(array('getProperties'))
+      ->setMethods(array('getFields'))
       ->getMockForAbstractClass();
     $content_entity->expects($this->once())
-      ->method('getProperties')
+      ->method('getFields')
       ->will($this->returnValue($definitions));
 
     $serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
index 5dcfd38..dc444aa 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -7,7 +7,9 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\Core\Entity\Annotation\EntityType;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper;
 use Drupal\Core\Field\FieldDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
@@ -280,10 +282,10 @@ protected function assertReadWrite($entity_type) {
     $entity->name[0]->setPropertyValues(array('value' => 'bar'));
     $this->assertEqual($entity->name->value, 'bar', format_string('%entity_type: Field value has been set via setPropertyValue()', array('%entity_type' => $entity_type)));
 
-    $values = $entity->getPropertyValues();
+    $values = $entity->toArray();
     $this->assertEqual($values['name'], array(0 => array('value' => 'bar')), format_string('%entity_type: Field value has been retrieved via getPropertyValue() from an entity.', array('%entity_type' => $entity_type)));
-    $entity->setPropertyValues(array('name' => 'foo'));
-    $this->assertEqual($entity->name->value, 'foo', format_string('%entity_type: Field value has been set via setPropertyValue() on an entity.', array('%entity_type' => $entity_type)));
+    //$entity->setPropertyValues(array('name' => 'foo'));
+    //$this->assertEqual($entity->name->value, 'foo', format_string('%entity_type: Field value has been set via setPropertyValue() on an entity.', array('%entity_type' => $entity_type)));
 
     // Make sure the user id can be set to zero.
     $user_item[0]['target_id'] = 0;
@@ -413,27 +415,28 @@ protected function checkIntrospection($entity_type) {
     $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type .': String processed property of the test-text field found.');
 
     // Make sure provided contextual information is right.
-    $this->assertIdentical($entity->getRoot(), $entity, 'Entity is root object.');
-    $this->assertEqual($entity->getPropertyPath(), '');
-    $this->assertEqual($entity->getName(), '');
-    $this->assertEqual($entity->getParent(), NULL);
+    $wrapper = new EntityTypedDataWrapper($entity);
+    $this->assertIdentical($wrapper->getRoot(), $wrapper, 'Entity is root object.');
+    $this->assertEqual($wrapper->getPropertyPath(), '');
+    $this->assertEqual($wrapper->getName(), '');
+    $this->assertEqual($wrapper->getParent(), NULL);
 
     $field = $entity->user_id;
-    $this->assertIdentical($field->getRoot(), $entity, 'Entity is root object.');
+    $this->assertIdentical($field->getRoot()->getEntity(), $entity, 'Entity is root object.');
     $this->assertIdentical($field->getEntity(), $entity, 'getEntity() returns the entity.');
     $this->assertEqual($field->getPropertyPath(), 'user_id');
     $this->assertEqual($field->getName(), 'user_id');
-    $this->assertIdentical($field->getParent(), $entity, 'Parent object matches.');
+    $this->assertIdentical($field->getParent()->getEntity(), $entity, 'Parent object matches.');
 
     $field_item = $field[0];
-    $this->assertIdentical($field_item->getRoot(), $entity, 'Entity is root object.');
+    $this->assertIdentical($field_item->getRoot()->getEntity(), $entity, 'Entity is root object.');
     $this->assertIdentical($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
     $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
     $this->assertEqual($field_item->getName(), '0');
     $this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.');
 
     $item_value = $field_item->get('entity');
-    $this->assertIdentical($item_value->getRoot(), $entity, 'Entity is root object.');
+    $this->assertIdentical($item_value->getRoot()->getEntity(), $entity, 'Entity is root object.');
     $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
     $this->assertEqual($item_value->getName(), 'entity');
     $this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.');
@@ -473,9 +476,9 @@ protected function assertIterator($entity_type) {
       }
     }
 
-    $properties = $entity->getProperties();
-    $this->assertEqual(array_keys($properties), array_keys($entity->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All properties returned.', array('%entity_type' => $entity_type)));
-    $this->assertEqual($properties, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all properties.', array('%entity_type' => $entity_type)));
+    $fields = $entity->getFields();
+    $this->assertEqual(array_keys($fields), array_keys($entity->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', array('%entity_type' => $entity_type)));
+    $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', array('%entity_type' => $entity_type)));
   }
 
   /**
@@ -501,7 +504,7 @@ protected function assertDataStructureInterfaces($entity_type) {
     // contained properties and getting all contained strings, limited by a
     // certain depth.
     $strings = array();
-    $this->getContainedStrings($entity, 0, $strings);
+    $this->getContainedStrings(new EntityTypedDataWrapper($entity), 0, $strings);
 
     // @todo: Once the user entity has defined properties this should contain
     // the user name and other user entity strings as well.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
index d3b5842..7dcbd6b 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
@@ -205,7 +205,10 @@ protected function _testMultilingualProperties($entity_type) {
           'user_id' => array(0 => $uid),
         );
       }
-      $entity->getTranslation($langcode)->setPropertyValues($properties[$langcode]);
+      $translation = $entity->getTranslation($langcode);
+      foreach ($properties[$langcode] as $field_name => $values) {
+        $translation->set($field_name, $values);
+      }
     }
     $entity->save();
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
index ec7134c..91722a7 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
@@ -86,7 +86,7 @@ protected function assertCRUD($entity_type) {
 
     // Creating a duplicate needs to result in a new UUID.
     $entity_duplicate = $entity->createDuplicate();
-    foreach ($entity->getProperties() as $property => $value) {
+    foreach ($entity->getFields() as $property => $value) {
       switch($property) {
         case 'uuid':
           $this->assertNotNull($entity_duplicate->uuid());
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
index 118abe9..6037c86 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
@@ -130,7 +130,7 @@ protected function checkValidation($entity_type) {
 
     // Make sure the information provided by a violation is correct.
     $violation = $violations[0];
-    $this->assertEqual($violation->getRoot(), $test_entity, 'Violation root is entity.');
+    $this->assertEqual($violation->getRoot()->getEntity(), $test_entity, 'Violation root is entity.');
     $this->assertEqual($violation->getPropertyPath(), 'name.0.value', 'Violation property path is correct.');
     $this->assertEqual($violation->getInvalidValue(), $test_entity->name->value, 'Violation contains invalid value.');
 
@@ -148,7 +148,7 @@ protected function checkValidation($entity_type) {
 
     // Make sure the information provided by a violation is correct.
     $violation = $violations[0];
-    $this->assertEqual($violation->getRoot(), $test_entity, 'Violation root is entity.');
+    $this->assertEqual($violation->getRoot()->getEntity(), $test_entity, 'Violation root is entity.');
     $this->assertEqual($violation->getPropertyPath(), 'field_test_text.0.format', 'Violation property path is correct.');
     $this->assertEqual($violation->getInvalidValue(), $test_entity->field_test_text->format, 'Violation contains invalid value.');
   }
diff --git a/core/modules/system/tests/modules/condition_test/lib/Drupal/condition_test/FormController.php b/core/modules/system/tests/modules/condition_test/lib/Drupal/condition_test/FormController.php
index 57c3009..92b4319 100644
--- a/core/modules/system/tests/modules/condition_test/lib/Drupal/condition_test/FormController.php
+++ b/core/modules/system/tests/modules/condition_test/lib/Drupal/condition_test/FormController.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\condition_test;
 
+use Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Condition\ConditionManager;
 
@@ -65,7 +66,7 @@ public function submitForm(array &$form, array &$form_state) {
     $bundles = implode(' and ', $config['bundles']);
     drupal_set_message(t('The bundles are @bundles', array('@bundles' => $bundles)));
     $article = node_load(1);
-    $this->condition->setContextValue('node', $article);
+    $this->condition->setContextValue('node', new EntityTypedDataWrapper($article));
     if ($this->condition->execute()) {
       drupal_set_message(t('Executed successfully.'));
     }
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Validation/Constraint/UserUniqueValidator.php b/core/modules/user/lib/Drupal/user/Plugin/Validation/Constraint/UserUniqueValidator.php
index f169cae..4d92787 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Validation/Constraint/UserUniqueValidator.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Validation/Constraint/UserUniqueValidator.php
@@ -20,7 +20,7 @@ class UserUniqueValidator extends ConstraintValidator {
    */
   public function validate($value, Constraint $constraint) {
     $field = $this->context->getMetadata()->getTypedData()->getParent();
-    $uid = $field->getParent()->id();
+    $uid = $field->getEntity()->id();
 
     $value_taken = (bool) \Drupal::entityQuery('user')
       // The UID could be NULL, so we cast it to 0 in that case.
