diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php
index c63817265588d34030e66e6e2bfd76d9dc02d368..d012f23de3df22b5e130abe2454fe2dc8f030c25 100644
--- a/app/code/Magento/Catalog/Model/Product.php
+++ b/app/code/Magento/Catalog/Model/Product.php
@@ -265,7 +265,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
     protected $productLinkFactory;
 
     /*
-     * @param \Magento\Catalog\Api\Data\ProductLinkExtensionInterfaceFactory
+     * @param \Magento\Catalog\Api\Data\ProductLinkExtensionFactory
      */
     protected $productLinkExtensionFactory;
 
@@ -331,7 +331,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
      * @param \Magento\Catalog\Model\ProductLink\CollectionProvider $entityCollectionProvider
      * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider
      * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory
-     * @param \Magento\Catalog\Api\Data\ProductLinkExtensionInterfaceFactory $productLinkExtensionFactory
+     * @param \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory
      * @param \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory
      * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
      * @param array $data
@@ -369,1008 +369,1008 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
         \Magento\Catalog\Model\ProductLink\CollectionProvider $entityCollectionProvider,
         \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider,
         \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory,
-        \Magento\Catalog\Api\Data\ProductLinkExtensionInterfaceFactory $productLinkExtensionFactory,
+        \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory,
         \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory,
         \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
         array $data = []
-        ) {
-            $this->metadataService = $metadataService;
-            $this->_itemOptionFactory = $itemOptionFactory;
-            $this->_stockItemFactory = $stockItemFactory;
-            $this->_optionInstance = $catalogProductOption;
-            $this->_catalogProductVisibility = $catalogProductVisibility;
-            $this->_catalogProductStatus = $catalogProductStatus;
-            $this->_catalogProductMediaConfig = $catalogProductMediaConfig;
-            $this->_catalogProductType = $catalogProductType;
-            $this->moduleManager = $moduleManager;
-            $this->_catalogProduct = $catalogProduct;
-            $this->_collectionFactory = $collectionFactory;
-            $this->_urlModel = $url;
-            $this->_linkInstance = $productLink;
-            $this->_filesystem = $filesystem;
-            $this->indexerRegistry = $indexerRegistry;
-            $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor;
-            $this->_productPriceIndexerProcessor = $productPriceIndexerProcessor;
-            $this->_productEavIndexerProcessor = $productEavIndexerProcessor;
-            $this->categoryRepository = $categoryRepository;
-            $this->imageCacheFactory = $imageCacheFactory;
-            $this->entityCollectionProvider = $entityCollectionProvider;
-            $this->linkTypeProvider = $linkTypeProvider;
-            $this->productLinkFactory = $productLinkFactory;
-            $this->productLinkExtensionFactory = $productLinkExtensionFactory;
-            $this->mediaGalleryEntryFactory = $mediaGalleryEntryFactory;
-            $this->dataObjectHelper = $dataObjectHelper;
-            parent::__construct(
-                $context,
-                $registry,
-                $extensionFactory,
-                $customAttributeFactory,
-                $storeManager,
-                $resource,
-                $resourceCollection,
-                $data
-            );
-        }
+    ) {
+        $this->metadataService = $metadataService;
+        $this->_itemOptionFactory = $itemOptionFactory;
+        $this->_stockItemFactory = $stockItemFactory;
+        $this->_optionInstance = $catalogProductOption;
+        $this->_catalogProductVisibility = $catalogProductVisibility;
+        $this->_catalogProductStatus = $catalogProductStatus;
+        $this->_catalogProductMediaConfig = $catalogProductMediaConfig;
+        $this->_catalogProductType = $catalogProductType;
+        $this->moduleManager = $moduleManager;
+        $this->_catalogProduct = $catalogProduct;
+        $this->_collectionFactory = $collectionFactory;
+        $this->_urlModel = $url;
+        $this->_linkInstance = $productLink;
+        $this->_filesystem = $filesystem;
+        $this->indexerRegistry = $indexerRegistry;
+        $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor;
+        $this->_productPriceIndexerProcessor = $productPriceIndexerProcessor;
+        $this->_productEavIndexerProcessor = $productEavIndexerProcessor;
+        $this->categoryRepository = $categoryRepository;
+        $this->imageCacheFactory = $imageCacheFactory;
+        $this->entityCollectionProvider = $entityCollectionProvider;
+        $this->linkTypeProvider = $linkTypeProvider;
+        $this->productLinkFactory = $productLinkFactory;
+        $this->productLinkExtensionFactory = $productLinkExtensionFactory;
+        $this->mediaGalleryEntryFactory = $mediaGalleryEntryFactory;
+        $this->dataObjectHelper = $dataObjectHelper;
+        parent::__construct(
+            $context,
+            $registry,
+            $extensionFactory,
+            $customAttributeFactory,
+            $storeManager,
+            $resource,
+            $resourceCollection,
+            $data
+        );
+    }
 
-        /**
-         * Initialize resources
-         *
-         * @return void
-         */
-        protected function _construct()
-        {
-            $this->_init('Magento\Catalog\Model\Resource\Product');
-        }
+    /**
+     * Initialize resources
+     *
+     * @return void
+     */
+    protected function _construct()
+    {
+        $this->_init('Magento\Catalog\Model\Resource\Product');
+    }
 
-        /**
-         * {@inheritdoc}
-         */
-        protected function getCustomAttributesCodes()
-        {
-            if ($this->customAttributesCodes === null) {
-                $this->customAttributesCodes = $this->getEavAttributesCodes($this->metadataService);
-                $this->customAttributesCodes = array_diff($this->customAttributesCodes, $this->interfaceAttributes);
-            }
-            return $this->customAttributesCodes;
+    /**
+     * {@inheritdoc}
+     */
+    protected function getCustomAttributesCodes()
+    {
+        if ($this->customAttributesCodes === null) {
+            $this->customAttributesCodes = $this->getEavAttributesCodes($this->metadataService);
+            $this->customAttributesCodes = array_diff($this->customAttributesCodes, $this->interfaceAttributes);
         }
+        return $this->customAttributesCodes;
+    }
 
-        /**
-         * Retrieve Store Id
-         *
-         * @return int
-         */
-        public function getStoreId()
-        {
-            if ($this->hasData(self::STORE_ID)) {
-                return $this->getData(self::STORE_ID);
-            }
-            return $this->_storeManager->getStore()->getId();
+    /**
+     * Retrieve Store Id
+     *
+     * @return int
+     */
+    public function getStoreId()
+    {
+        if ($this->hasData(self::STORE_ID)) {
+            return $this->getData(self::STORE_ID);
         }
+        return $this->_storeManager->getStore()->getId();
+    }
 
-        /**
-         * Get collection instance
-         *
-         * @return object
-         */
-        public function getResourceCollection()
-        {
-            $collection = parent::getResourceCollection();
-            $collection->setStoreId($this->getStoreId());
-            return $collection;
-        }
+    /**
+     * Get collection instance
+     *
+     * @return object
+     */
+    public function getResourceCollection()
+    {
+        $collection = parent::getResourceCollection();
+        $collection->setStoreId($this->getStoreId());
+        return $collection;
+    }
 
-        /**
-         * Get product url model
-         *
-         * @return Product\Url
-         */
-        public function getUrlModel()
-        {
-            return $this->_urlModel;
-        }
+    /**
+     * Get product url model
+     *
+     * @return Product\Url
+     */
+    public function getUrlModel()
+    {
+        return $this->_urlModel;
+    }
 
-        /**
-         * Validate Product Data
-         *
-         * @todo implement full validation process with errors returning which are ignoring now
-         *
-         * @return array
-         */
-        public function validate()
-        {
-            $this->_eventManager->dispatch($this->_eventPrefix . '_validate_before', $this->_getEventData());
-            $result = $this->_getResource()->validate($this);
-            $this->_eventManager->dispatch($this->_eventPrefix . '_validate_after', $this->_getEventData());
-            return $result;
-        }
+    /**
+     * Validate Product Data
+     *
+     * @todo implement full validation process with errors returning which are ignoring now
+     *
+     * @return array
+     */
+    public function validate()
+    {
+        $this->_eventManager->dispatch($this->_eventPrefix . '_validate_before', $this->_getEventData());
+        $result = $this->_getResource()->validate($this);
+        $this->_eventManager->dispatch($this->_eventPrefix . '_validate_after', $this->_getEventData());
+        return $result;
+    }
 
-        /**
-         * Get product name
-         *
-         * @return string
-         * @codeCoverageIgnoreStart
-         */
-        public function getName()
-        {
-            return $this->_getData(self::NAME);
-        }
-        //@codeCoverageIgnoreEnd
+    /**
+     * Get product name
+     *
+     * @return string
+     * @codeCoverageIgnoreStart
+     */
+    public function getName()
+    {
+        return $this->_getData(self::NAME);
+    }
+    //@codeCoverageIgnoreEnd
 
-        /**
-         * Get product price through type instance
-         *
-         * @return float
-         */
-        public function getPrice()
-        {
-            if ($this->_calculatePrice || !$this->getData(self::PRICE)) {
-                return $this->getPriceModel()->getPrice($this);
-            } else {
-                return $this->getData(self::PRICE);
-            }
+    /**
+     * Get product price through type instance
+     *
+     * @return float
+     */
+    public function getPrice()
+    {
+        if ($this->_calculatePrice || !$this->getData(self::PRICE)) {
+            return $this->getPriceModel()->getPrice($this);
+        } else {
+            return $this->getData(self::PRICE);
         }
+    }
 
-        /**
-         * @codeCoverageIgnoreStart
-         * Get visibility status
-         * @see \Magento\Catalog\Model\Product\Visibility
-         *
-         * @return int
-         */
-        public function getVisibility()
-        {
-            return $this->_getData(self::VISIBILITY);
-        }
+    /**
+     * @codeCoverageIgnoreStart
+     * Get visibility status
+     * @see \Magento\Catalog\Model\Product\Visibility
+     *
+     * @return int
+     */
+    public function getVisibility()
+    {
+        return $this->_getData(self::VISIBILITY);
+    }
 
-        /**
-         * Get product attribute set id
-         *
-         * @return int
-         */
-        public function getAttributeSetId()
-        {
-            return $this->_getData(self::ATTRIBUTE_SET_ID);
-        }
+    /**
+     * Get product attribute set id
+     *
+     * @return int
+     */
+    public function getAttributeSetId()
+    {
+        return $this->_getData(self::ATTRIBUTE_SET_ID);
+    }
 
-        /**
-         * Get product creation date
-         *
-         * @return string
-         */
-        public function getCreatedAt()
-        {
-            return $this->_getData(self::CREATED_AT);
-        }
+    /**
+     * Get product creation date
+     *
+     * @return string
+     */
+    public function getCreatedAt()
+    {
+        return $this->_getData(self::CREATED_AT);
+    }
 
-        /**
-         * Get previous product update date
-         *
-         * @return string
-         */
-        public function getUpdatedAt()
-        {
-            return $this->_getData(self::UPDATED_AT);
+    /**
+     * Get previous product update date
+     *
+     * @return string
+     */
+    public function getUpdatedAt()
+    {
+        return $this->_getData(self::UPDATED_AT);
+    }
+
+    /**
+     * Set Price calculation flag
+     *
+     * @param bool $calculate
+     * @return void
+     */
+    public function setPriceCalculation($calculate = true)
+    {
+        $this->_calculatePrice = $calculate;
+    }
+
+    /**
+     * Get product type identifier
+     *
+     * @return array|string
+     */
+    public function getTypeId()
+    {
+        return $this->_getData(self::TYPE_ID);
+    }
+    //@codeCoverageIgnoreEnd
+
+    /**
+     * Get product status
+     *
+     * @return int
+     */
+    public function getStatus()
+    {
+        if ($this->_getData(self::STATUS) === null) {
+            $this->setData(self::STATUS, \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED);
         }
+        return $this->_getData(self::STATUS);
+    }
 
-        /**
-         * Set Price calculation flag
-         *
-         * @param bool $calculate
-         * @return void
-         */
-        public function setPriceCalculation($calculate = true)
-        {
-            $this->_calculatePrice = $calculate;
+    /**
+     * Retrieve type instance of the product.
+     * Type instance implements product type depended logic and is a singleton shared by all products of the same type.
+     *
+     * @return \Magento\Catalog\Model\Product\Type\AbstractType
+     */
+    public function getTypeInstance()
+    {
+        if ($this->_typeInstance === null) {
+            $this->_typeInstance = $this->_catalogProductType->factory($this);
         }
+        return $this->_typeInstance;
+    }
 
-        /**
-         * Get product type identifier
-         *
-         * @return array|string
-         */
-        public function getTypeId()
-        {
-            return $this->_getData(self::TYPE_ID);
+    /**
+     * Set type instance for the product
+     *
+     * @param \Magento\Catalog\Model\Product\Type\AbstractType|null $instance  Product type instance
+     * @return \Magento\Catalog\Model\Product
+     */
+    public function setTypeInstance($instance)
+    {
+        $this->_typeInstance = $instance;
+        return $this;
+    }
+
+    /**
+     * Retrieve link instance
+     *
+     * @return  Product\Link
+     */
+    public function getLinkInstance()
+    {
+        return $this->_linkInstance;
+    }
+
+    /**
+     * Retrieve product id by sku
+     *
+     * @param   string $sku
+     * @return  integer
+     */
+    public function getIdBySku($sku)
+    {
+        return $this->_getResource()->getIdBySku($sku);
+    }
+
+    /**
+     * Retrieve product category id
+     *
+     * @return int
+     */
+    public function getCategoryId()
+    {
+        $category = $this->_registry->registry('current_category');
+        if ($category) {
+            return $category->getId();
         }
-        //@codeCoverageIgnoreEnd
+        return false;
+    }
 
-        /**
-         * Get product status
-         *
-         * @return int
-         */
-        public function getStatus()
-        {
-            if ($this->_getData(self::STATUS) === null) {
-                $this->setData(self::STATUS, \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED);
-            }
-            return $this->_getData(self::STATUS);
+    /**
+     * Retrieve product category
+     *
+     * @return \Magento\Catalog\Model\Category
+     */
+    public function getCategory()
+    {
+        $category = $this->getData('category');
+        if ($category === null && $this->getCategoryId()) {
+            $category = $this->categoryRepository->get($this->getCategoryId());
+            $this->setCategory($category);
         }
+        return $category;
+    }
 
-        /**
-         * Retrieve type instance of the product.
-         * Type instance implements product type depended logic and is a singleton shared by all products of the same type.
-         *
-         * @return \Magento\Catalog\Model\Product\Type\AbstractType
-         */
-        public function getTypeInstance()
-        {
-            if ($this->_typeInstance === null) {
-                $this->_typeInstance = $this->_catalogProductType->factory($this);
+    /**
+     * Retrieve assigned category Ids
+     *
+     * @return array
+     */
+    public function getCategoryIds()
+    {
+        if (!$this->hasData('category_ids')) {
+            $wasLocked = false;
+            if ($this->isLockedAttribute('category_ids')) {
+                $wasLocked = true;
+                $this->unlockAttribute('category_ids');
+            }
+            $ids = $this->_getResource()->getCategoryIds($this);
+            $this->setData('category_ids', $ids);
+            if ($wasLocked) {
+                $this->lockAttribute('category_ids');
             }
-            return $this->_typeInstance;
         }
 
-        /**
-         * Set type instance for the product
-         *
-         * @param \Magento\Catalog\Model\Product\Type\AbstractType|null $instance  Product type instance
-         * @return \Magento\Catalog\Model\Product
-         */
-        public function setTypeInstance($instance)
-        {
-            $this->_typeInstance = $instance;
-            return $this;
-        }
+        return (array) $this->_getData('category_ids');
+    }
 
-        /**
-         * Retrieve link instance
-         *
-         * @return  Product\Link
-         */
-        public function getLinkInstance()
-        {
-            return $this->_linkInstance;
-        }
+    /**
+     * Retrieve product categories
+     *
+     * @return \Magento\Framework\Data\Collection
+     */
+    public function getCategoryCollection()
+    {
+        return $this->_getResource()->getCategoryCollection($this);
+    }
 
-        /**
-         * Retrieve product id by sku
-         *
-         * @param   string $sku
-         * @return  integer
-         */
-        public function getIdBySku($sku)
-        {
-            return $this->_getResource()->getIdBySku($sku);
+    /**
+     * Retrieve product websites identifiers
+     *
+     * @return array
+     */
+    public function getWebsiteIds()
+    {
+        if (!$this->hasWebsiteIds()) {
+            $ids = $this->_getResource()->getWebsiteIds($this);
+            $this->setWebsiteIds($ids);
         }
+        return $this->getData('website_ids');
+    }
 
-        /**
-         * Retrieve product category id
-         *
-         * @return int
-         */
-        public function getCategoryId()
-        {
-            $category = $this->_registry->registry('current_category');
-            if ($category) {
-                return $category->getId();
+    /**
+     * Get all sore ids where product is presented
+     *
+     * @return array
+     */
+    public function getStoreIds()
+    {
+        if (!$this->hasStoreIds()) {
+            $storeIds = [];
+            if ($websiteIds = $this->getWebsiteIds()) {
+                foreach ($websiteIds as $websiteId) {
+                    $websiteStores = $this->_storeManager->getWebsite($websiteId)->getStoreIds();
+                    $storeIds = array_merge($storeIds, $websiteStores);
+                }
             }
-            return false;
+            $this->setStoreIds($storeIds);
         }
+        return $this->getData('store_ids');
+    }
 
-        /**
-         * Retrieve product category
-         *
-         * @return \Magento\Catalog\Model\Category
-         */
-        public function getCategory()
-        {
-            $category = $this->getData('category');
-            if ($category === null && $this->getCategoryId()) {
-                $category = $this->categoryRepository->get($this->getCategoryId());
-                $this->setCategory($category);
+    /**
+     * Retrieve product attributes
+     * if $groupId is null - retrieve all product attributes
+     *
+     * @param int  $groupId   Retrieve attributes of the specified group
+     * @param bool $skipSuper Not used
+     * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute[]
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function getAttributes($groupId = null, $skipSuper = false)
+    {
+        $productAttributes = $this->getTypeInstance()->getEditableAttributes($this);
+        if ($groupId) {
+            $attributes = [];
+            foreach ($productAttributes as $attribute) {
+                if ($attribute->isInGroup($this->getAttributeSetId(), $groupId)) {
+                    $attributes[] = $attribute;
+                }
             }
-            return $category;
+        } else {
+            $attributes = $productAttributes;
         }
 
-        /**
-         * Retrieve assigned category Ids
-         *
-         * @return array
-         */
-        public function getCategoryIds()
-        {
-            if (!$this->hasData('category_ids')) {
-                $wasLocked = false;
-                if ($this->isLockedAttribute('category_ids')) {
-                    $wasLocked = true;
-                    $this->unlockAttribute('category_ids');
-                }
-                $ids = $this->_getResource()->getCategoryIds($this);
-                $this->setData('category_ids', $ids);
-                if ($wasLocked) {
-                    $this->lockAttribute('category_ids');
-                }
-            }
+        return $attributes;
+    }
 
-            return (array) $this->_getData('category_ids');
-        }
+    /**
+     * Check product options and type options and save them, too
+     *
+     * @return void
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     */
+    public function beforeSave()
+    {
+        $this->cleanCache();
+        $this->setTypeHasOptions(false);
+        $this->setTypeHasRequiredOptions(false);
 
-        /**
-         * Retrieve product categories
-         *
-         * @return \Magento\Framework\Data\Collection
-         */
-        public function getCategoryCollection()
-        {
-            return $this->_getResource()->getCategoryCollection($this);
-        }
+        $this->getTypeInstance()->beforeSave($this);
 
-        /**
-         * Retrieve product websites identifiers
-         *
-         * @return array
-         */
-        public function getWebsiteIds()
-        {
-            if (!$this->hasWebsiteIds()) {
-                $ids = $this->_getResource()->getWebsiteIds($this);
-                $this->setWebsiteIds($ids);
-            }
-            return $this->getData('website_ids');
-        }
+        $hasOptions = false;
+        $hasRequiredOptions = false;
 
         /**
-         * Get all sore ids where product is presented
-         *
-         * @return array
+         * $this->_canAffectOptions - set by type instance only
+         * $this->getCanSaveCustomOptions() - set either in controller when "Custom Options" ajax tab is loaded,
+         * or in type instance as well
          */
-        public function getStoreIds()
-        {
-            if (!$this->hasStoreIds()) {
-                $storeIds = [];
-                if ($websiteIds = $this->getWebsiteIds()) {
-                    foreach ($websiteIds as $websiteId) {
-                        $websiteStores = $this->_storeManager->getWebsite($websiteId)->getStoreIds();
-                        $storeIds = array_merge($storeIds, $websiteStores);
+        $this->canAffectOptions($this->_canAffectOptions && $this->getCanSaveCustomOptions());
+        if ($this->getCanSaveCustomOptions()) {
+            $options = $this->getProductOptions();
+            if (is_array($options)) {
+                $this->setIsCustomOptionChanged(true);
+                foreach ($this->getProductOptions() as $option) {
+                    $this->getOptionInstance()->addOption($option);
+                    if (!isset($option['is_delete']) || $option['is_delete'] != '1') {
+                        $hasOptions = true;
+                    }
+                }
+                foreach ($this->getOptionInstance()->getOptions() as $option) {
+                    if ($option['is_require'] == '1') {
+                        $hasRequiredOptions = true;
+                        break;
                     }
                 }
-                $this->setStoreIds($storeIds);
             }
-            return $this->getData('store_ids');
         }
 
         /**
-         * Retrieve product attributes
-         * if $groupId is null - retrieve all product attributes
-         *
-         * @param int  $groupId   Retrieve attributes of the specified group
-         * @param bool $skipSuper Not used
-         * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute[]
-         * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+         * Set true, if any
+         * Set false, ONLY if options have been affected by Options tab and Type instance tab
          */
-        public function getAttributes($groupId = null, $skipSuper = false)
-        {
-            $productAttributes = $this->getTypeInstance()->getEditableAttributes($this);
-            if ($groupId) {
-                $attributes = [];
-                foreach ($productAttributes as $attribute) {
-                    if ($attribute->isInGroup($this->getAttributeSetId(), $groupId)) {
-                        $attributes[] = $attribute;
-                    }
-                }
-            } else {
-                $attributes = $productAttributes;
+        if ($hasOptions || (bool)$this->getTypeHasOptions()) {
+            $this->setHasOptions(true);
+            if ($hasRequiredOptions || (bool)$this->getTypeHasRequiredOptions()) {
+                $this->setRequiredOptions(true);
+            } elseif ($this->canAffectOptions()) {
+                $this->setRequiredOptions(false);
             }
+        } elseif ($this->canAffectOptions()) {
+            $this->setHasOptions(false);
+            $this->setRequiredOptions(false);
+        }
 
-            return $attributes;
+        if (!$this->getOrigData('website_ids')) {
+            $websiteIds = $this->_getResource()->getWebsiteIds($this);
+            $this->setOrigData('website_ids', $websiteIds);
         }
+        parent::beforeSave();
+    }
 
-        /**
-         * Check product options and type options and save them, too
-         *
-         * @return void
-         * @SuppressWarnings(PHPMD.CyclomaticComplexity)
-         */
-        public function beforeSave()
-        {
-            $this->cleanCache();
-            $this->setTypeHasOptions(false);
-            $this->setTypeHasRequiredOptions(false);
-
-            $this->getTypeInstance()->beforeSave($this);
-
-            $hasOptions = false;
-            $hasRequiredOptions = false;
-
-            /**
-             * $this->_canAffectOptions - set by type instance only
-             * $this->getCanSaveCustomOptions() - set either in controller when "Custom Options" ajax tab is loaded,
-             * or in type instance as well
-             */
-            $this->canAffectOptions($this->_canAffectOptions && $this->getCanSaveCustomOptions());
-            if ($this->getCanSaveCustomOptions()) {
-                $options = $this->getProductOptions();
-                if (is_array($options)) {
-                    $this->setIsCustomOptionChanged(true);
-                    foreach ($this->getProductOptions() as $option) {
-                        $this->getOptionInstance()->addOption($option);
-                        if (!isset($option['is_delete']) || $option['is_delete'] != '1') {
-                            $hasOptions = true;
-                        }
-                    }
-                    foreach ($this->getOptionInstance()->getOptions() as $option) {
-                        if ($option['is_require'] == '1') {
-                            $hasRequiredOptions = true;
-                            break;
-                        }
-                    }
-                }
-            }
+    /**
+     * Check/set if options can be affected when saving product
+     * If value specified, it will be set.
+     *
+     * @param   bool $value
+     * @return  bool
+     */
+    public function canAffectOptions($value = null)
+    {
+        if (null !== $value) {
+            $this->_canAffectOptions = (bool) $value;
+        }
+        return $this->_canAffectOptions;
+    }
 
-            /**
-             * Set true, if any
-             * Set false, ONLY if options have been affected by Options tab and Type instance tab
-             */
-            if ($hasOptions || (bool)$this->getTypeHasOptions()) {
-                $this->setHasOptions(true);
-                if ($hasRequiredOptions || (bool)$this->getTypeHasRequiredOptions()) {
-                    $this->setRequiredOptions(true);
-                } elseif ($this->canAffectOptions()) {
-                    $this->setRequiredOptions(false);
-                }
-            } elseif ($this->canAffectOptions()) {
-                $this->setHasOptions(false);
-                $this->setRequiredOptions(false);
-            }
+    /**
+     * Saving product type related data and init index
+     *
+     * @return \Magento\Catalog\Model\Product
+     */
+    public function afterSave()
+    {
+        $this->getLinkInstance()->saveProductRelations($this);
+        $this->getTypeInstance()->save($this);
 
-            if (!$this->getOrigData('website_ids')) {
-                $websiteIds = $this->_getResource()->getWebsiteIds($this);
-                $this->setOrigData('website_ids', $websiteIds);
-            }
-            parent::beforeSave();
+        if ($this->getStockData()) {
+            $this->setForceReindexEavRequired(true);
         }
 
-        /**
-         * Check/set if options can be affected when saving product
-         * If value specified, it will be set.
-         *
-         * @param   bool $value
-         * @return  bool
-         */
-        public function canAffectOptions($value = null)
-        {
-            if (null !== $value) {
-                $this->_canAffectOptions = (bool) $value;
-            }
-            return $this->_canAffectOptions;
-        }
+        $this->_getResource()->addCommitCallback([$this, 'priceReindexCallback']);
+        $this->_getResource()->addCommitCallback([$this, 'eavReindexCallback']);
 
         /**
-         * Saving product type related data and init index
-         *
-         * @return \Magento\Catalog\Model\Product
+         * Product Options
          */
-        public function afterSave()
-        {
-            $this->getLinkInstance()->saveProductRelations($this);
-            $this->getTypeInstance()->save($this);
+        if (!$this->getIsDuplicate()) {
+            $this->getOptionInstance()->setProduct($this)->saveOptions();
+        }
 
-            if ($this->getStockData()) {
-                $this->setForceReindexEavRequired(true);
-            }
+        $result = parent::afterSave();
 
-            $this->_getResource()->addCommitCallback([$this, 'priceReindexCallback']);
-            $this->_getResource()->addCommitCallback([$this, 'eavReindexCallback']);
+        $this->_getResource()->addCommitCallback([$this, 'reindex']);
+        $this->reloadPriceInfo();
 
-            /**
-             * Product Options
-             */
-            if (!$this->getIsDuplicate()) {
-                $this->getOptionInstance()->setProduct($this)->saveOptions();
-            }
+        // Resize images for catalog product and save results to image cache
+        /** @var Product\Image\Cache $imageCache */
+        $imageCache = $this->imageCacheFactory->create();
+        $imageCache->generate($this);
 
-            $result = parent::afterSave();
+        return $result;
+    }
 
-            $this->_getResource()->addCommitCallback([$this, 'reindex']);
+    /**
+     * Set quantity for product
+     *
+     * @param float $qty
+     * @return $this
+     */
+    public function setQty($qty)
+    {
+        if ($this->getData('qty') != $qty) {
+            $this->setData('qty', $qty);
             $this->reloadPriceInfo();
-
-            // Resize images for catalog product and save results to image cache
-            /** @var Product\Image\Cache $imageCache */
-            $imageCache = $this->imageCacheFactory->create();
-            $imageCache->generate($this);
-
-            return $result;
-        }
-
-        /**
-         * Set quantity for product
-         *
-         * @param float $qty
-         * @return $this
-         */
-        public function setQty($qty)
-        {
-            if ($this->getData('qty') != $qty) {
-                $this->setData('qty', $qty);
-                $this->reloadPriceInfo();
-            }
-            return $this;
         }
+        return $this;
+    }
 
-        /**
-         * Get quantity for product
-         *
-         * @return float
-         */
-        public function getQty()
-        {
-            return $this->getData('qty');
-        }
+    /**
+     * Get quantity for product
+     *
+     * @return float
+     */
+    public function getQty()
+    {
+        return $this->getData('qty');
+    }
 
-        /**
-         * Callback for entity reindex
-         *
-         * @return void
-         */
-        public function priceReindexCallback()
-        {
-            if ($this->isObjectNew() || $this->_catalogProduct->isDataForPriceIndexerWasChanged($this)) {
-                $this->_productPriceIndexerProcessor->reindexRow($this->getEntityId());
-            }
+    /**
+     * Callback for entity reindex
+     *
+     * @return void
+     */
+    public function priceReindexCallback()
+    {
+        if ($this->isObjectNew() || $this->_catalogProduct->isDataForPriceIndexerWasChanged($this)) {
+            $this->_productPriceIndexerProcessor->reindexRow($this->getEntityId());
         }
+    }
 
-        /**
-         * Reindex callback for EAV indexer
-         *
-         * @return void
-         */
-        public function eavReindexCallback()
-        {
-            if ($this->isObjectNew() || $this->hasDataChanges()) {
-                $this->_productEavIndexerProcessor->reindexRow($this->getEntityId());
-            }
+    /**
+     * Reindex callback for EAV indexer
+     *
+     * @return void
+     */
+    public function eavReindexCallback()
+    {
+        if ($this->isObjectNew() || $this->hasDataChanges()) {
+            $this->_productEavIndexerProcessor->reindexRow($this->getEntityId());
         }
+    }
 
-        /**
-         * Init indexing process after product save
-         *
-         * @return void
-         */
-        public function reindex()
-        {
-            if ($this->_catalogProduct->isDataForProductCategoryIndexerWasChanged($this) || $this->isDeleted()) {
-                $productCategoryIndexer = $this->indexerRegistry->get(Indexer\Product\Category::INDEXER_ID);
-                if (!$productCategoryIndexer->isScheduled()) {
-                    $productCategoryIndexer->reindexRow($this->getId());
-                }
+    /**
+     * Init indexing process after product save
+     *
+     * @return void
+     */
+    public function reindex()
+    {
+        if ($this->_catalogProduct->isDataForProductCategoryIndexerWasChanged($this) || $this->isDeleted()) {
+            $productCategoryIndexer = $this->indexerRegistry->get(Indexer\Product\Category::INDEXER_ID);
+            if (!$productCategoryIndexer->isScheduled()) {
+                $productCategoryIndexer->reindexRow($this->getId());
             }
-            $this->_productFlatIndexerProcessor->reindexRow($this->getEntityId());
         }
+        $this->_productFlatIndexerProcessor->reindexRow($this->getEntityId());
+    }
 
-        /**
-         * Clear cache related with product and protect delete from not admin
-         * Register indexing event before delete product
-         *
-         * @return \Magento\Catalog\Model\Product
-         */
-        public function beforeDelete()
-        {
-            $this->cleanCache();
-            return parent::beforeDelete();
-        }
+    /**
+     * Clear cache related with product and protect delete from not admin
+     * Register indexing event before delete product
+     *
+     * @return \Magento\Catalog\Model\Product
+     */
+    public function beforeDelete()
+    {
+        $this->cleanCache();
+        return parent::beforeDelete();
+    }
 
-        /**
-         * Init indexing process after product delete commit
-         *
-         * @return void
-         */
-        public function afterDeleteCommit()
-        {
-            $this->reindex();
-            $this->_productPriceIndexerProcessor->reindexRow($this->getId());
-            parent::afterDeleteCommit();
-        }
+    /**
+     * Init indexing process after product delete commit
+     *
+     * @return void
+     */
+    public function afterDeleteCommit()
+    {
+        $this->reindex();
+        $this->_productPriceIndexerProcessor->reindexRow($this->getId());
+        parent::afterDeleteCommit();
+    }
 
+    /**
+     * Load product options if they exists
+     *
+     * @return $this
+     */
+    protected function _afterLoad()
+    {
+        parent::_afterLoad();
         /**
-         * Load product options if they exists
-         *
-         * @return $this
+         * Load product options
          */
-        protected function _afterLoad()
-        {
-            parent::_afterLoad();
-            /**
-             * Load product options
-             */
-            if ($this->getHasOptions()) {
-                foreach ($this->getProductOptionsCollection() as $option) {
-                    $option->setProduct($this);
-                    $this->addOption($option);
-                }
+        if ($this->getHasOptions()) {
+            foreach ($this->getProductOptionsCollection() as $option) {
+                $option->setProduct($this);
+                $this->addOption($option);
             }
-            return $this;
         }
+        return $this;
+    }
 
-        /**
-         * Clear cache related with product id
-         *
-         * @return $this
-         */
-        public function cleanCache()
-        {
-            $this->_cacheManager->clean('catalog_product_' . $this->getId());
-            return $this;
-        }
+    /**
+     * Clear cache related with product id
+     *
+     * @return $this
+     */
+    public function cleanCache()
+    {
+        $this->_cacheManager->clean('catalog_product_' . $this->getId());
+        return $this;
+    }
 
-        /**
-         * Get product price model
-         *
-         * @return \Magento\Catalog\Model\Product\Type\Price
-         */
-        public function getPriceModel()
-        {
-            return $this->_catalogProductType->priceFactory($this->getTypeId());
-        }
+    /**
+     * Get product price model
+     *
+     * @return \Magento\Catalog\Model\Product\Type\Price
+     */
+    public function getPriceModel()
+    {
+        return $this->_catalogProductType->priceFactory($this->getTypeId());
+    }
 
-        /**
-         * Get product Price Info object
-         *
-         * @return \Magento\Framework\Pricing\PriceInfo\Base
-         */
-        public function getPriceInfo()
-        {
-            if (!$this->_priceInfo) {
-                $this->_priceInfo = $this->_catalogProductType->getPriceInfo($this);
-            }
-            return $this->_priceInfo;
+    /**
+     * Get product Price Info object
+     *
+     * @return \Magento\Framework\Pricing\PriceInfo\Base
+     */
+    public function getPriceInfo()
+    {
+        if (!$this->_priceInfo) {
+            $this->_priceInfo = $this->_catalogProductType->getPriceInfo($this);
         }
+        return $this->_priceInfo;
+    }
 
-        /**
-         * Get product group price for the customer
-         *
-         * @return float
-         */
-        public function getGroupPrice()
-        {
-            return $this->getPriceModel()->getGroupPrice($this);
-        }
+    /**
+     * Get product group price for the customer
+     *
+     * @return float
+     */
+    public function getGroupPrice()
+    {
+        return $this->getPriceModel()->getGroupPrice($this);
+    }
 
-        /**
-         * Gets list of product group prices
-         *
-         * @return \Magento\Catalog\Api\Data\ProductGroupPriceInterface[]|null
-         */
-        public function getGroupPrices()
-        {
-            return $this->getPriceModel()->getGroupPrices($this);
-        }
+    /**
+     * Gets list of product group prices
+     *
+     * @return \Magento\Catalog\Api\Data\ProductGroupPriceInterface[]|null
+     */
+    public function getGroupPrices()
+    {
+        return $this->getPriceModel()->getGroupPrices($this);
+    }
 
-        /**
-         * Sets list of product group prices
-         *
-         * @param \Magento\Catalog\Api\Data\ProductGroupPriceInterface[] $groupPrices
-         * @return $this
-         */
-        public function setGroupPrices(array $groupPrices = null)
-        {
-            $this->getPriceModel()->setGroupPrices($this, $groupPrices);
-            return $this;
-        }
+    /**
+     * Sets list of product group prices
+     *
+     * @param \Magento\Catalog\Api\Data\ProductGroupPriceInterface[] $groupPrices
+     * @return $this
+     */
+    public function setGroupPrices(array $groupPrices = null)
+    {
+        $this->getPriceModel()->setGroupPrices($this, $groupPrices);
+        return $this;
+    }
 
-        /**
-         * Gets list of product tier prices
-         *
-         * @return \Magento\Catalog\Api\Data\ProductTierPriceInterface[]|null
-         */
-        public function getTierPrices()
-        {
-            return $this->getPriceModel()->getTierPrices($this);
-        }
+    /**
+     * Gets list of product tier prices
+     *
+     * @return \Magento\Catalog\Api\Data\ProductTierPriceInterface[]|null
+     */
+    public function getTierPrices()
+    {
+        return $this->getPriceModel()->getTierPrices($this);
+    }
 
-        /**
-         * Sets list of product tier prices
-         *
-         * @param \Magento\Catalog\Api\Data\ProductTierPriceInterface[] $tierPrices
-         * @return $this
-         */
-        public function setTierPrices(array $tierPrices = null)
-        {
-            $this->getPriceModel()->setTierPrices($this, $tierPrices);
-            return $this;
-        }
+    /**
+     * Sets list of product tier prices
+     *
+     * @param \Magento\Catalog\Api\Data\ProductTierPriceInterface[] $tierPrices
+     * @return $this
+     */
+    public function setTierPrices(array $tierPrices = null)
+    {
+        $this->getPriceModel()->setTierPrices($this, $tierPrices);
+        return $this;
+    }
 
-        /**
-         * Get product tier price for the customer, based on qty of this product
-         *
-         * @param   float $qty
-         * @return  float|array
-         * @deprecated (MAGETWO-31465)
-         */
-        public function getTierPrice($qty = null)
-        {
-            return $this->getPriceModel()->getTierPrice($qty, $this);
-        }
+    /**
+     * Get product tier price for the customer, based on qty of this product
+     *
+     * @param   float $qty
+     * @return  float|array
+     * @deprecated (MAGETWO-31465)
+     */
+    public function getTierPrice($qty = null)
+    {
+        return $this->getPriceModel()->getTierPrice($qty, $this);
+    }
 
-        /**
-         * Get formatted by currency product price
-         *
-         * @return  array || double
-         */
-        public function getFormatedPrice()
-        {
-            return $this->getPriceModel()->getFormatedPrice($this);
-        }
+    /**
+     * Get formatted by currency product price
+     *
+     * @return  array || double
+     */
+    public function getFormatedPrice()
+    {
+        return $this->getPriceModel()->getFormatedPrice($this);
+    }
 
-        /**
-         * Sets final price of product
-         *
-         * This func is equal to magic 'setFinalPrice()', but added as a separate func, because in cart with bundle
-         * products it's called very often in Item->getProduct(). So removing chain of magic with more cpu consuming
-         * algorithms gives nice optimization boost.
-         *
-         * @param float $price Price amount
-         * @return \Magento\Catalog\Model\Product
-         */
-        public function setFinalPrice($price)
-        {
-            $this->_data['final_price'] = $price;
-            return $this;
-        }
+    /**
+     * Sets final price of product
+     *
+     * This func is equal to magic 'setFinalPrice()', but added as a separate func, because in cart with bundle
+     * products it's called very often in Item->getProduct(). So removing chain of magic with more cpu consuming
+     * algorithms gives nice optimization boost.
+     *
+     * @param float $price Price amount
+     * @return \Magento\Catalog\Model\Product
+     */
+    public function setFinalPrice($price)
+    {
+        $this->_data['final_price'] = $price;
+        return $this;
+    }
 
-        /**
-         * Get product final price
-         *
-         * @param float $qty
-         * @return float
-         */
-        public function getFinalPrice($qty = null)
-        {
-            $price = $this->_getData('final_price');
-            if ($price !== null) {
-                return $price;
-            }
-            return $this->getPriceModel()->getFinalPrice($qty, $this);
+    /**
+     * Get product final price
+     *
+     * @param float $qty
+     * @return float
+     */
+    public function getFinalPrice($qty = null)
+    {
+        $price = $this->_getData('final_price');
+        if ($price !== null) {
+            return $price;
         }
+        return $this->getPriceModel()->getFinalPrice($qty, $this);
+    }
 
-        /**
-         * Returns calculated final price
-         *
-         * @return float
-         */
-        public function getCalculatedFinalPrice()
-        {
-            return $this->_getData('calculated_final_price');
-        }
+    /**
+     * Returns calculated final price
+     *
+     * @return float
+     */
+    public function getCalculatedFinalPrice()
+    {
+        return $this->_getData('calculated_final_price');
+    }
 
-        /**
-         * Returns minimal price
-         *
-         * @return float
-         */
-        public function getMinimalPrice()
-        {
-            return max($this->_getData('minimal_price'), 0);
-        }
+    /**
+     * Returns minimal price
+     *
+     * @return float
+     */
+    public function getMinimalPrice()
+    {
+        return max($this->_getData('minimal_price'), 0);
+    }
 
-        /**
-         * Returns special price
-         *
-         * @return float
-         */
-        public function getSpecialPrice()
-        {
-            return $this->_getData('special_price');
-        }
+    /**
+     * Returns special price
+     *
+     * @return float
+     */
+    public function getSpecialPrice()
+    {
+        return $this->_getData('special_price');
+    }
 
-        /**
-         * Returns starting date of the special price
-         *
-         * @return mixed
-         */
-        public function getSpecialFromDate()
-        {
-            return $this->_getData('special_from_date');
-        }
+    /**
+     * Returns starting date of the special price
+     *
+     * @return mixed
+     */
+    public function getSpecialFromDate()
+    {
+        return $this->_getData('special_from_date');
+    }
 
-        /**
-         * Returns end date of the special price
-         *
-         * @return mixed
-         */
-        public function getSpecialToDate()
-        {
-            return $this->_getData('special_to_date');
-        }
+    /**
+     * Returns end date of the special price
+     *
+     * @return mixed
+     */
+    public function getSpecialToDate()
+    {
+        return $this->_getData('special_to_date');
+    }
 
-        /*******************************************************************************
-         ** Linked products API
-         */
-        /**
-         * Retrieve array of related products
-         *
-         * @return array
-         */
-        public function getRelatedProducts()
-        {
-            if (!$this->hasRelatedProducts()) {
-                $products = [];
-                $collection = $this->getRelatedProductCollection();
-                foreach ($collection as $product) {
-                    $products[] = $product;
-                }
-                $this->setRelatedProducts($products);
+    /*******************************************************************************
+     ** Linked products API
+     */
+    /**
+     * Retrieve array of related products
+     *
+     * @return array
+     */
+    public function getRelatedProducts()
+    {
+        if (!$this->hasRelatedProducts()) {
+            $products = [];
+            $collection = $this->getRelatedProductCollection();
+            foreach ($collection as $product) {
+                $products[] = $product;
             }
-            return $this->getData('related_products');
+            $this->setRelatedProducts($products);
         }
+        return $this->getData('related_products');
+    }
 
-        /**
-         * Retrieve related products identifiers
-         *
-         * @return array
-         */
-        public function getRelatedProductIds()
-        {
-            if (!$this->hasRelatedProductIds()) {
-                $ids = [];
-                foreach ($this->getRelatedProducts() as $product) {
-                    $ids[] = $product->getId();
-                }
-                $this->setRelatedProductIds($ids);
+    /**
+     * Retrieve related products identifiers
+     *
+     * @return array
+     */
+    public function getRelatedProductIds()
+    {
+        if (!$this->hasRelatedProductIds()) {
+            $ids = [];
+            foreach ($this->getRelatedProducts() as $product) {
+                $ids[] = $product->getId();
             }
-            return $this->getData('related_product_ids');
+            $this->setRelatedProductIds($ids);
         }
+        return $this->getData('related_product_ids');
+    }
 
-        /**
-         * Retrieve collection related product
-         *
-         * @return \Magento\Catalog\Model\Resource\Product\Link\Product\Collection
-         */
-        public function getRelatedProductCollection()
-        {
-            $collection = $this->getLinkInstance()->useRelatedLinks()->getProductCollection()->setIsStrongMode();
-            $collection->setProduct($this);
-            return $collection;
-        }
+    /**
+     * Retrieve collection related product
+     *
+     * @return \Magento\Catalog\Model\Resource\Product\Link\Product\Collection
+     */
+    public function getRelatedProductCollection()
+    {
+        $collection = $this->getLinkInstance()->useRelatedLinks()->getProductCollection()->setIsStrongMode();
+        $collection->setProduct($this);
+        return $collection;
+    }
 
-        /**
-         * Retrieve collection related link
-         *
-         * @return \Magento\Catalog\Model\Resource\Product\Link\Collection
-         */
-        public function getRelatedLinkCollection()
-        {
-            $collection = $this->getLinkInstance()->useRelatedLinks()->getLinkCollection();
-            $collection->setProduct($this);
-            $collection->addLinkTypeIdFilter();
-            $collection->addProductIdFilter();
-            $collection->joinAttributes();
-            return $collection;
-        }
+    /**
+     * Retrieve collection related link
+     *
+     * @return \Magento\Catalog\Model\Resource\Product\Link\Collection
+     */
+    public function getRelatedLinkCollection()
+    {
+        $collection = $this->getLinkInstance()->useRelatedLinks()->getLinkCollection();
+        $collection->setProduct($this);
+        $collection->addLinkTypeIdFilter();
+        $collection->addProductIdFilter();
+        $collection->joinAttributes();
+        return $collection;
+    }
 
-        /**
-         * Retrieve array of up sell products
-         *
-         * @return array
-         */
-        public function getUpSellProducts()
-        {
-            if (!$this->hasUpSellProducts()) {
-                $products = [];
-                foreach ($this->getUpSellProductCollection() as $product) {
-                    $products[] = $product;
-                }
-                $this->setUpSellProducts($products);
+    /**
+     * Retrieve array of up sell products
+     *
+     * @return array
+     */
+    public function getUpSellProducts()
+    {
+        if (!$this->hasUpSellProducts()) {
+            $products = [];
+            foreach ($this->getUpSellProductCollection() as $product) {
+                $products[] = $product;
             }
-            return $this->getData('up_sell_products');
+            $this->setUpSellProducts($products);
         }
+        return $this->getData('up_sell_products');
+    }
 
-        /**
-         * Retrieve up sell products identifiers
-         *
-         * @return array
-         */
-        public function getUpSellProductIds()
-        {
-            if (!$this->hasUpSellProductIds()) {
-                $ids = [];
-                foreach ($this->getUpSellProducts() as $product) {
-                    $ids[] = $product->getId();
-                }
-                $this->setUpSellProductIds($ids);
+    /**
+     * Retrieve up sell products identifiers
+     *
+     * @return array
+     */
+    public function getUpSellProductIds()
+    {
+        if (!$this->hasUpSellProductIds()) {
+            $ids = [];
+            foreach ($this->getUpSellProducts() as $product) {
+                $ids[] = $product->getId();
             }
-            return $this->getData('up_sell_product_ids');
+            $this->setUpSellProductIds($ids);
         }
+        return $this->getData('up_sell_product_ids');
+    }
 
-        /**
-         * Retrieve collection up sell product
-         *
-         * @return \Magento\Catalog\Model\Resource\Product\Link\Product\Collection
-         */
-        public function getUpSellProductCollection()
-        {
-            $collection = $this->getLinkInstance()->useUpSellLinks()->getProductCollection()->setIsStrongMode();
-            $collection->setProduct($this);
-            return $collection;
-        }
+    /**
+     * Retrieve collection up sell product
+     *
+     * @return \Magento\Catalog\Model\Resource\Product\Link\Product\Collection
+     */
+    public function getUpSellProductCollection()
+    {
+        $collection = $this->getLinkInstance()->useUpSellLinks()->getProductCollection()->setIsStrongMode();
+        $collection->setProduct($this);
+        return $collection;
+    }
 
-        /**
-         * Retrieve collection up sell link
-         *
-         * @return \Magento\Catalog\Model\Resource\Product\Link\Collection
-         */
-        public function getUpSellLinkCollection()
-        {
-            $collection = $this->getLinkInstance()->useUpSellLinks()->getLinkCollection();
-            $collection->setProduct($this);
-            $collection->addLinkTypeIdFilter();
-            $collection->addProductIdFilter();
-            $collection->joinAttributes();
-            return $collection;
-        }
+    /**
+     * Retrieve collection up sell link
+     *
+     * @return \Magento\Catalog\Model\Resource\Product\Link\Collection
+     */
+    public function getUpSellLinkCollection()
+    {
+        $collection = $this->getLinkInstance()->useUpSellLinks()->getLinkCollection();
+        $collection->setProduct($this);
+        $collection->addLinkTypeIdFilter();
+        $collection->addProductIdFilter();
+        $collection->joinAttributes();
+        return $collection;
+    }
 
-        /**
-         * Retrieve array of cross sell products
-         *
-         * @return array
-         */
-        public function getCrossSellProducts()
-        {
-            if (!$this->hasCrossSellProducts()) {
-                $products = [];
-                foreach ($this->getCrossSellProductCollection() as $product) {
-                    $products[] = $product;
-                }
-                $this->setCrossSellProducts($products);
+    /**
+     * Retrieve array of cross sell products
+     *
+     * @return array
+     */
+    public function getCrossSellProducts()
+    {
+        if (!$this->hasCrossSellProducts()) {
+            $products = [];
+            foreach ($this->getCrossSellProductCollection() as $product) {
+                $products[] = $product;
             }
-            return $this->getData('cross_sell_products');
+            $this->setCrossSellProducts($products);
         }
+        return $this->getData('cross_sell_products');
+    }
 
-        /**
-         * Retrieve cross sell products identifiers
-         *
-         * @return array
-         */
-        public function getCrossSellProductIds()
-        {
-            if (!$this->hasCrossSellProductIds()) {
-                $ids = [];
-                foreach ($this->getCrossSellProducts() as $product) {
-                    $ids[] = $product->getId();
-                }
-                $this->setCrossSellProductIds($ids);
+    /**
+     * Retrieve cross sell products identifiers
+     *
+     * @return array
+     */
+    public function getCrossSellProductIds()
+    {
+        if (!$this->hasCrossSellProductIds()) {
+            $ids = [];
+            foreach ($this->getCrossSellProducts() as $product) {
+                $ids[] = $product->getId();
             }
-            return $this->getData('cross_sell_product_ids');
+            $this->setCrossSellProductIds($ids);
         }
+        return $this->getData('cross_sell_product_ids');
+    }
 
-        /**
-         * Retrieve collection cross sell product
-         *
-         * @return \Magento\Catalog\Model\Resource\Product\Link\Product\Collection
-         */
-        public function getCrossSellProductCollection()
-        {
-            $collection = $this->getLinkInstance()->useCrossSellLinks()->getProductCollection()->setIsStrongMode();
-            $collection->setProduct($this);
-            return $collection;
-        }
+    /**
+     * Retrieve collection cross sell product
+     *
+     * @return \Magento\Catalog\Model\Resource\Product\Link\Product\Collection
+     */
+    public function getCrossSellProductCollection()
+    {
+        $collection = $this->getLinkInstance()->useCrossSellLinks()->getProductCollection()->setIsStrongMode();
+        $collection->setProduct($this);
+        return $collection;
+    }
 
-        /**
-         * Retrieve collection cross sell link
-         *
-         * @return \Magento\Catalog\Model\Resource\Product\Link\Collection
-         */
-        public function getCrossSellLinkCollection()
-        {
-            $collection = $this->getLinkInstance()->useCrossSellLinks()->getLinkCollection();
-            $collection->setProduct($this);
-            $collection->addLinkTypeIdFilter();
-            $collection->addProductIdFilter();
-            $collection->joinAttributes();
-            return $collection;
-        }
+    /**
+     * Retrieve collection cross sell link
+     *
+     * @return \Magento\Catalog\Model\Resource\Product\Link\Collection
+     */
+    public function getCrossSellLinkCollection()
+    {
+        $collection = $this->getLinkInstance()->useCrossSellLinks()->getLinkCollection();
+        $collection->setProduct($this);
+        $collection->addLinkTypeIdFilter();
+        $collection->addProductIdFilter();
+        $collection->joinAttributes();
+        return $collection;
+    }
 
-        /**
-         * Get product links info
-         *
-         * @return \Magento\Catalog\Api\Data\ProductLinkInterface[]|null
-         */
-        public function getProductLinks()
-        {
-            if (empty($this->_links)) {
-                $output = [];
-                $linkTypes = $this->linkTypeProvider->getLinkTypes();
-            foreach($linkTypes as $linkTypeName => $linkTypeValue) {
+    /**
+     * Get product links info
+     *
+     * @return \Magento\Catalog\Api\Data\ProductLinkInterface[]|null
+     */
+    public function getProductLinks()
+    {
+        if (empty($this->_links)) {
+            $output = [];
+            $linkTypes = $this->linkTypeProvider->getLinkTypes();
+            foreach (array_keys($linkTypes) as $linkTypeName) {
                 $collection = $this->entityCollectionProvider->getCollection($this, $linkTypeName);
                 foreach ($collection as $item) {
                     /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */
@@ -1391,7 +1391,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
                             $setterName = 'set' . ucfirst($name);
                             // Check if setter exists
                             if (method_exists($productLinkExtension, $setterName)) {
-                                call_user_func(array($productLinkExtension, $setterName), $value);
+                                call_user_func([$productLinkExtension, $setterName], $value);
                             }
                         }
                         $productLink->setExtensionAttributes($productLinkExtension);
diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php
index 5a34c26b577d7da459ed99f6326694b8b04990ab..8f2e7664c4e3df2c3ebf963cd4122e22a0e730cb 100644
--- a/app/code/Magento/Catalog/Model/ProductRepository.php
+++ b/app/code/Magento/Catalog/Model/ProductRepository.php
@@ -373,7 +373,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
 
         // Clear all existing product links and then set the ones we want
         $linkTypes = $this->linkTypeProvider->getLinkTypes();
-        foreach($linkTypes as $typeName => $typeValue) {
+        foreach (array_keys($linkTypes) as $typeName) {
             $this->linkInitializer->initializeLinks($product, [$typeName => []]);
         }
 
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
index 32d3768d2659d61a68e9190dad466b95ac9169bd..35b260532553ee98240c049b12e1116449fbc9d9 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
@@ -121,6 +121,9 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $objectManager;
 
+    /**
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
     protected function setUp()
     {
         $this->productFactoryMock = $this->getMock('Magento\Catalog\Model\ProductFactory', ['create'], [], '', false);