Completed
Pull Request — master (#4)
by Nate
03:04
created

ReflectionTypeAdapter   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 18
lcom 1
cbo 12
dl 0
loc 158
ccs 56
cts 56
cp 1
rs 10
c 1
b 0
f 0

3 Methods

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