Passed
Push — master ( cb4b42...276006 )
by Nate
45s
created

ReflectionTypeAdapter   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 161
c 1
b 0
f 0
wmc 18
lcom 1
cbo 13
ccs 58
cts 58
cp 1
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
C read() 0 47 10
C write() 0 41 7
1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
namespace Tebru\Gson\Internal\TypeAdapter;
8
9
use Tebru\Gson\Annotation\JsonAdapter;
10
use Tebru\Gson\ClassMetadata;
11
use Tebru\Gson\Internal\Data\AnnotationSet;
12
use Tebru\Gson\Internal\Data\MetadataPropertyCollection;
13
use Tebru\Gson\Internal\Data\Property;
14
use Tebru\Gson\Internal\DefaultExclusionData;
15
use Tebru\Gson\Internal\Excluder;
16
use Tebru\Gson\Internal\ObjectConstructor\CreateFromInstance;
17
use Tebru\Gson\Internal\ObjectConstructorAware;
18
use Tebru\Gson\Internal\ObjectConstructorAwareTrait;
19
use Tebru\Gson\Internal\TypeAdapterProvider;
20
use Tebru\Gson\JsonWritable;
21
use Tebru\Gson\Internal\Data\PropertyCollection;
22
use Tebru\Gson\JsonReadable;
23
use Tebru\Gson\Internal\ObjectConstructor;
24
use Tebru\Gson\JsonToken;
25
use Tebru\Gson\TypeAdapter;
26
27
/**
28
 * Class ReflectionTypeAdapter
29
 *
30
 * Uses reflected class properties to read/write object
31
 *
32
 * @author Nate Brunette <[email protected]>
33
 */
34
final class ReflectionTypeAdapter extends TypeAdapter implements ObjectConstructorAware
35
{
36
    use ObjectConstructorAwareTrait;
37
38
    /**
39
     * @var PropertyCollection
40
     */
41
    private $properties;
42
43
    /**
44
     * @var MetadataPropertyCollection
45
     */
46
    private $metadataPropertyCollection;
47
48
    /**
49
     * @var ClassMetadata
50
     */
51
    private $classMetadata;
52
53
    /**
54
     * @var Excluder
55
     */
56
    private $excluder;
57
58
    /**
59
     * @var TypeAdapterProvider
60
     */
61
    private $typeAdapterProvider;
62
63
    /**
64
     * Constructor
65
     *
66
     * @param ObjectConstructor $objectConstructor
67
     * @param PropertyCollection $properties
68
     * @param MetadataPropertyCollection $metadataPropertyCollection
69
     * @param ClassMetadata $classMetadata
70
     * @param Excluder $excluder
71
     * @param TypeAdapterProvider $typeAdapterProvider
72
     */
73 7
    public function __construct(
74
        ObjectConstructor $objectConstructor,
75
        PropertyCollection $properties,
76
        MetadataPropertyCollection $metadataPropertyCollection,
77
        ClassMetadata $classMetadata,
78
        Excluder $excluder,
79
        TypeAdapterProvider $typeAdapterProvider
80
    ) {
81 7
        $this->objectConstructor = $objectConstructor;
82 7
        $this->properties = $properties;
83 7
        $this->metadataPropertyCollection = $metadataPropertyCollection;
84 7
        $this->classMetadata = $classMetadata;
85 7
        $this->excluder = $excluder;
86 7
        $this->typeAdapterProvider = $typeAdapterProvider;
87 7
    }
88
    /**
89
     * Read the next value, convert it to its type and return it
90
     *
91
     * @param JsonReadable $reader
92
     * @return object
93
     * @throws \InvalidArgumentException
94
     * @throws \Tebru\PhpType\Exception\MalformedTypeException If the type cannot be parsed
95
     */
96 4
    public function read(JsonReadable $reader)
97
    {
98 4
        if ($reader->peek() === JsonToken::NULL) {
99 1
            return $reader->nextNull();
100
        }
101
102 3
        if ($this->excluder->excludeClassByStrategy($this->classMetadata, false)) {
103 1
            $reader->skipValue();
104
105 1
            return null;
106
        }
107
108 2
        $object = $this->objectConstructor->construct();
109 2
        $exclusionData = new DefaultExclusionData(false, clone $object, $reader->getPayload());
110
111 2
        $reader->beginObject();
112 2
        while ($reader->hasNext()) {
113 2
            $name = $reader->nextName();
114 2
            $property = $this->properties->getBySerializedName($name);
115
            if (
116 2
                null === $property
117 2
                || $property->skipDeserialize()
118 2
                || $this->excluder->excludePropertyByStrategy($this->metadataPropertyCollection->get($property->getRealName()), $exclusionData)
119
            ) {
120 2
                $reader->skipValue();
121 2
                continue;
122
            }
123
124
            /** @var JsonAdapter $jsonAdapterAnnotation */
125 2
            $jsonAdapterAnnotation = $property->getAnnotations()->getAnnotation(JsonAdapter::class, AnnotationSet::TYPE_PROPERTY);
126 2
            $adapter = null === $jsonAdapterAnnotation
127 2
                ? $this->typeAdapterProvider->getAdapter($property->getType())
128 2
                : $this->typeAdapterProvider->getAdapterFromAnnotation($property->getType(), $jsonAdapterAnnotation);
129
130 2
            if ($adapter instanceof ObjectConstructorAware) {
131 2
                $nestedObject = $property->get($object);
132 2
                if ($nestedObject !== null) {
133 1
                    $adapter->setObjectConstructor(new CreateFromInstance($nestedObject));
134
                }
135
            }
136
137 2
            $property->set($object, $adapter->read($reader));
138
        }
139 2
        $reader->endObject();
140
141 2
        return $object;
142
    }
143
144
    /**
145
     * Write the value to the writer for the type
146
     *
147
     * @param JsonWritable $writer
148
     * @param object $value
149
     * @return void
150
     * @throws \InvalidArgumentException
151
     * @throws \Tebru\PhpType\Exception\MalformedTypeException If the type cannot be parsed
152
     */
153 3
    public function write(JsonWritable $writer, $value): void
154
    {
155 3
        if (null === $value) {
156 1
            $writer->writeNull();
157
158 1
            return;
159
        }
160
161 2
        if ($this->excluder->excludeClassByStrategy($this->classMetadata, true)) {
162 1
            $writer->writeNull();
163
164 1
            return;
165
        }
166
167 1
        $exclusionData = new DefaultExclusionData(true, $value);
168
169 1
        $writer->beginObject();
170
171
        /** @var Property $property */
172 1
        foreach ($this->properties as $property) {
173 1
            $writer->name($property->getSerializedName());
174
175
            if (
176 1
                $property->skipSerialize()
177 1
                || $this->excluder->excludePropertyByStrategy($this->metadataPropertyCollection->get($property->getRealName()), $exclusionData)
178
            ) {
179 1
                $writer->writeNull();
180
181 1
                continue;
182
            }
183
184
            /** @var JsonAdapter $jsonAdapterAnnotation */
185 1
            $jsonAdapterAnnotation = $property->getAnnotations()->getAnnotation(JsonAdapter::class, AnnotationSet::TYPE_PROPERTY);
186 1
            $adapter = null === $jsonAdapterAnnotation
187 1
                ? $this->typeAdapterProvider->getAdapter($property->getType())
188 1
                : $this->typeAdapterProvider->getAdapterFromAnnotation($property->getType(), $jsonAdapterAnnotation);
189 1
            $adapter->write($writer, $property->get($value));
190
        }
191
192 1
        $writer->endObject();
193 1
    }
194
}
195