Completed
Push — 21.x ( bb9c8e )
by Tim
01:32
created

prepareDynamicMediaGalleryAttributes()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 1
nop 0
crap 6
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Media\Observers\MediaGalleryObserver
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import-product-media
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Media\Observers;
22
23
use TechDivision\Import\Product\Media\Utils\ColumnKeys;
24
use TechDivision\Import\Product\Media\Utils\MemberNames;
25
use TechDivision\Import\Product\Media\Utils\EntityTypeCodes;
26
use TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface;
27
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
28
use TechDivision\Import\Observers\StateDetectorInterface;
29
use TechDivision\Import\Observers\AttributeLoaderInterface;
30
use TechDivision\Import\Observers\DynamicAttributeObserverInterface;
31
32
/**
33
 * Observer that creates/updates the product's media gallery information.
34
 *
35
 * @author    Tim Wagner <[email protected]>
36
 * @copyright 2016 TechDivision GmbH <[email protected]>
37
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
38
 * @link      https://github.com/techdivision/import-product-media
39
 * @link      http://www.techdivision.com
40
 */
41
class MediaGalleryObserver extends AbstractProductImportObserver implements DynamicAttributeObserverInterface
42
{
43
44
    /**
45
     * The media gallery attribute code.
46
     *
47
     * @var string
48
     */
49
    const ATTRIBUTE_CODE = 'media_gallery';
50
51
    /**
52
     * The ID of the parent product the media is related to.
53
     *
54
     * @var integer
55
     */
56
    protected $parentId;
57
58
    /**
59
     * The ID of the persisted media gallery entity.
60
     *
61
     * @var integer
62
     */
63
    protected $valueId;
64
65
    /**
66
     * The product media processor instance.
67
     *
68
     * @var \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface
69
     */
70
    protected $productMediaProcessor;
71
72
    /**
73
     * The attribute loader instance.
74
     *
75
     * @var \TechDivision\Import\Observers\AttributeLoaderInterface
76
     */
77
    protected $attributeLoader;
78
79
    /**
80
     * Initialize the observer with the passed product media processor instance.
81
     *
82
     * @param \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface $productMediaProcessor The product media processor instance
83
     * @param \TechDivision\Import\Observers\AttributeLoaderInterface                    $attributeLoader       The attribute loader instance
84
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null                 $stateDetector         The state detector instance to use
85
     */
86
    public function __construct(
87
        ProductMediaProcessorInterface $productMediaProcessor,
88
        AttributeLoaderInterface $attributeLoader = null,
89
        StateDetectorInterface $stateDetector = null
90
    ) {
91
92
        // initialize the media processor and the dynamic attribute loader instance
93
        $this->productMediaProcessor = $productMediaProcessor;
94
        $this->attributeLoader = $attributeLoader;
95
96
        // pass the state detector to the parent method
97
        parent::__construct($stateDetector);
98
    }
99
100
    /**
101
     * Return's the product media processor instance.
102
     *
103
     * @return \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface The product media processor instance
104
     */
105
    protected function getProductMediaProcessor()
106
    {
107
        return $this->productMediaProcessor;
108
    }
109
110
    /**
111
     * Process the observer's business logic.
112
     *
113
     * @return array The processed row
114
     */
115
    protected function process()
116
    {
117
118
        // try to load the product SKU and map it the entity ID and
119
        $this->parentId = $this->getValue(ColumnKeys::IMAGE_PARENT_SKU, null, array($this, 'mapParentSku'));
120
121
        // reset the position counter, if either a new PK or store view code has been found
122
        if (!$this->isParentStoreViewCode($this->getValue(ColumnKeys::STORE_VIEW_CODE, $this->getDefaultStoreViewCode())) ||
123
            !$this->isParentId($this->parentId)
124
        ) {
125
            $this->resetPositionCounter();
126
        }
127
128
        // prepare the actual store view code
129
        $this->prepareStoreViewCode($this->getRow());
0 ignored issues
show
Unused Code introduced by
The call to MediaGalleryObserver::prepareStoreViewCode() has too many arguments starting with $this->getRow().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
130
131
        // initialize and persist the product media gallery
132
        if ($this->hasChanges($productMediaGallery = $this->initializeProductMediaGallery($this->prepareDynamicMediaGalleryAttributes()))) {
133
            // persist the media gallery data and temporarily persist value ID
134
            $this->setParentValueId($this->valueId = $this->persistProductMediaGallery($productMediaGallery));
0 ignored issues
show
Documentation Bug introduced by
The property $valueId was declared of type integer, but $this->persistProductMed...y($productMediaGallery) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
135
            // persist the product media gallery to entity data
136
            if ($productMediaGalleryValueToEntity = $this->initializeProductMediaGalleryValueToEntity($this->prepareProductMediaGalleryValueToEntityAttributes())) {
137
                $this->persistProductMediaGalleryValueToEntity($productMediaGalleryValueToEntity);
138
            }
139
        }
140
141
        // temporarily persist parent ID
142
        $this->setParentId($this->parentId);
143
    }
144
145
    /**
146
     * Appends the dynamic to the static attributes for the media type
147
     * gallery attributes and returns them.
148
     *
149
     * @return array The array with all available attributes
150
     */
151
    protected function prepareDynamicMediaGalleryAttributes()
152
    {
153
        return array_merge($this->prepareProductMediaGalleryAttributes(), $this->attributeLoader ? $this->attributeLoader->load($this, array()) : array());
154
    }
155
156
    /**
157
     * Prepare the product media gallery that has to be persisted.
158
     *
159
     * @return array The prepared product media gallery attributes
160
     */
161
    protected function prepareProductMediaGalleryAttributes()
162
    {
163
164
        // load the attribute ID of the media gallery EAV attribute
165
        $mediaGalleryAttribute = $this->getEavAttributeByAttributeCode(MediaGalleryObserver::ATTRIBUTE_CODE);
166
        $attributeId = $mediaGalleryAttribute[MemberNames::ATTRIBUTE_ID];
167
168
        // initialize the gallery data
169
        $disabled = 0;
170
        $mediaType = $this->getValue(ColumnKeys::MEDIA_TYPE, 'image');
171
        $image = $this->getValue(ColumnKeys::IMAGE_PATH_NEW);
172
173
        // initialize and return the entity
174
        return $this->initializeEntity(
175
            $this->loadRawEntity(
176
                array(
177
                    MemberNames::ATTRIBUTE_ID => $attributeId,
178
                    MemberNames::VALUE        => $image,
179
                    MemberNames::MEDIA_TYPE   => $mediaType,
180
                    MemberNames::DISABLED     => $disabled
181
                )
182
            )
183
        );
184
    }
185
186
    /**
187
     * Prepare the product media gallery value to entity that has to be persisted.
188
     *
189
     * @return array The prepared product media gallery value to entity attributes
190
     */
191
    protected function prepareProductMediaGalleryValueToEntityAttributes()
192
    {
193
194
        // initialize and return the entity
195
        return $this->initializeEntity(
196
            array(
197
                MemberNames::VALUE_ID  => $this->valueId,
198
                MemberNames::ENTITY_ID => $this->parentId
199
            )
200
        );
201
    }
202
203
    /**
204
     * Load's and return's a raw customer entity without primary key but the mandatory members only and nulled values.
205
     *
206
     * @param array $data An array with data that will be used to initialize the raw entity with
207
     *
208
     * @return array The initialized entity
209
     */
210
    protected function loadRawEntity(array $data = array())
211
    {
212
        return $this->getProductMediaProcessor()->loadRawEntity(EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY, $data);
213
    }
214
215
    /**
216
     * Initialize the product media gallery with the passed attributes and returns an instance.
217
     *
218
     * @param array $attr The product media gallery attributes
219
     *
220
     * @return array The initialized product media gallery
221
     */
222
    protected function initializeProductMediaGallery(array $attr)
223
    {
224
        return $attr;
225
    }
226
227
    /**
228
     * Initialize the product media gallery value to entity with the passed attributes and returns an instance.
229
     *
230
     * @param array $attr The product media gallery value to entity attributes
231
     *
232
     * @return array|null The initialized product media gallery value to entity, or NULL if the product media gallery value to entity already exists
233
     */
234
    protected function initializeProductMediaGalleryValueToEntity(array $attr)
235
    {
236
        return $attr;
237
    }
238
239
    /**
240
     * Map's the passed SKU of the parent product to it's PK.
241
     *
242
     * @param string $parentSku The SKU of the parent product
243
     *
244
     * @return integer The primary key used to create relations
245
     */
246
    protected function mapParentSku($parentSku)
247
    {
248
        return $this->mapSkuToEntityId($parentSku);
249
    }
250
251
    /**
252
     * Set's the value ID of the created media gallery entry.
253
     *
254
     * @param integer $parentValueId The ID of the created media gallery entry
255
     *
256
     * @return void
257
     */
258
    protected function setParentValueId($parentValueId)
259
    {
260
        $this->getSubject()->setParentValueId($parentValueId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method setParentValueId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
261
    }
262
263
    /**
264
     * Return the entity ID for the passed SKU.
265
     *
266
     * @param string $sku The SKU to return the entity ID for
267
     *
268
     * @return integer The mapped entity ID
269
     * @throws \Exception Is thrown if the SKU is not mapped yet
270
     */
271
    protected function mapSkuToEntityId($sku)
272
    {
273
        return $this->getSubject()->mapSkuToEntityId($sku);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method mapSkuToEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
274
    }
275
276
    /**
277
     * Set's the ID of the parent product to relate the variant with.
278
     *
279
     * @param integer $parentId The ID of the parent product
280
     *
281
     * @return void
282
     */
283
    protected function setParentId($parentId)
284
    {
285
        $this->getSubject()->setParentId($parentId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method setParentId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
286
    }
287
288
    /**
289
     * Return's the ID of the parent product to relate the variant with.
290
     *
291
     * @return integer The ID of the parent product
292
     */
293
    protected function getParentId()
294
    {
295
        return $this->getSubject()->getParentId();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getParentId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
296
    }
297
298
    /**
299
     * Query whether or not this is the parent ID.
300
     *
301
     * @param integer $parentId The PK of the parent image
302
     *
303
     * @return boolean TRUE if the PK euqals, else FALSE
304
     */
305
    protected function isParentId($parentId)
306
    {
307
        return $this->getParentId() === $parentId;
308
    }
309
310
    /**
311
     * Query whether or not this is the parent store view code.
312
     *
313
     * @param string $storeViewCode The actual store view code
314
     *
315
     * @return boolean TRUE if the store view code equals, else FALSE
316
     */
317
    protected function isParentStoreViewCode($storeViewCode)
318
    {
319
        return $this->getStoreViewCode() === $storeViewCode;
320
    }
321
322
    /**
323
     * Return's the default store view code.
324
     *
325
     * @return array The default store view code
326
     */
327
    protected function getDefaultStoreViewCode()
328
    {
329
        return $this->getSubject()->getDefaultStoreViewCode();
330
    }
331
332
    /**
333
     * Reset the position counter to 1.
334
     *
335
     * @return void
336
     */
337
    protected function resetPositionCounter()
338
    {
339
        $this->getSubject()->resetPositionCounter();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method resetPositionCounter() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
340
    }
341
342
    /**
343
     * Return's the EAV attribute with the passed attribute code.
344
     *
345
     * @param string $attributeCode The attribute code
346
     *
347
     * @return array The array with the EAV attribute
348
     * @throws \Exception Is thrown if the attribute with the passed code is not available
349
     */
350
    protected function getEavAttributeByAttributeCode($attributeCode)
351
    {
352
        return $this->getSubject()->getEavAttributeByAttributeCode($attributeCode);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getEavAttributeByAttributeCode() does only exist in the following implementations of said interface: TechDivision\Import\Observers\EntitySubjectImpl, TechDivision\Import\Prod...a\Subjects\MediaSubject, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\AbstractEavSubject, TechDivision\Import\Subjects\ValidatorSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
353
    }
354
355
    /**
356
     * Persist's the passed product media gallery data and return's the ID.
357
     *
358
     * @param array $productMediaGallery The product media gallery data to persist
359
     *
360
     * @return string The ID of the persisted entity
361
     */
362
    protected function persistProductMediaGallery($productMediaGallery)
363
    {
364
        return $this->getProductMediaProcessor()->persistProductMediaGallery($productMediaGallery);
365
    }
366
367
    /**
368
     * Persist's the passed product media gallery value to entity data.
369
     *
370
     * @param array $productMediaGalleryValueToEntity The product media gallery value to entity data to persist
371
     *
372
     * @return void
373
     */
374
    protected function persistProductMediaGalleryValueToEntity($productMediaGalleryValueToEntity)
375
    {
376
        $this->getProductMediaProcessor()->persistProductMediaGalleryValueToEntity($productMediaGalleryValueToEntity);
377
    }
378
}
379