TypeAdapterProvider   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 111
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 35
dl 0
loc 111
ccs 35
cts 35
cp 1
rs 10
c 2
b 0
f 0
wmc 15

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B getAdapter() 0 28 7
B getAdapterFromAnnotation() 0 27 7
1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
declare(strict_types=1);
8
9
namespace Tebru\Gson\Internal;
10
11
use InvalidArgumentException;
12
use Tebru\Gson\Annotation\JsonAdapter;
13
use Tebru\Gson\JsonDeserializer;
14
use Tebru\Gson\JsonSerializer;
15
use Tebru\Gson\TypeAdapter;
16
use Tebru\Gson\TypeAdapter\CustomWrappedTypeAdapter;
17
use Tebru\Gson\TypeAdapterFactory;
18
use Tebru\PhpType\TypeToken;
19
20
/**
21
 * Class TypeAdapterProvider
22
 *
23
 * @author Nate Brunette <[email protected]>
24
 */
25
final class TypeAdapterProvider
26
{
27
    /**
28
     * A cache of created type adapters
29
     *
30
     * @var TypeAdapter[]
31
     */
32
    private $typeAdapters = [];
33
34
    /**
35
     * All registered [@see TypeAdapter] factories
36
     *
37
     * @var TypeAdapterFactory[]
38
     */
39
    private $typeAdapterFactories;
40
41
    /**
42
     * @var ConstructorConstructor
43
     */
44
    private $constructorConstructor;
45
46
    /**
47
     * Constructor
48
     *
49
     * @param array $typeAdapterFactories
50
     * @param ConstructorConstructor $constructorConstructor
51
     */
52 20
    public function __construct(array $typeAdapterFactories, ConstructorConstructor $constructorConstructor)
53
    {
54 20
        $this->typeAdapterFactories = $typeAdapterFactories;
55 20
        $this->constructorConstructor = $constructorConstructor;
56 20
    }
57
58
    /**
59
     * Creates a key based on the type, and optionally the class that should be skipped.
60
     * Returns the [@see TypeAdapter] if it has already been created, otherwise loops
61
     * over all of the factories and finds a type adapter that supports the type.
62
     *
63
     * @param TypeToken $type
64
     * @param TypeAdapterFactory|null $skip
65
     * @return TypeAdapter
66
     * @throws InvalidArgumentException if the type cannot be handled by a type adapter
67
     */
68 10
    public function getAdapter(TypeToken $type, TypeAdapterFactory $skip = null): TypeAdapter
69
    {
70 10
        $key = (string)$type;
71 10
        if (null === $skip && isset($this->typeAdapters[$key])) {
72 2
            return $this->typeAdapters[$key];
73
        }
74
75 10
        foreach ($this->typeAdapterFactories as $typeAdapterFactory) {
76 9
            if ($typeAdapterFactory === $skip) {
77 1
                continue;
78
            }
79
80 8
            $adapter = $typeAdapterFactory->create($type, $this);
81 8
            if ($adapter === null) {
82 5
                continue;
83
            }
84
85
            // do not save skipped adapters
86 8
            if ($skip === null) {
87 8
                $this->typeAdapters[$key] = $adapter;
88
            }
89
90 8
            return $adapter;
91
        }
92
93 2
        throw new InvalidArgumentException(sprintf(
94 2
            'The type "%s" could not be handled by any of the registered type adapters',
95 2
            (string)$type
96
        ));
97
    }
98
99
    /**
100
     * Get a type adapter from a [@see JsonAdapter] annotation
101
     *
102
     * The class may be a TypeAdapter, TypeAdapterFactory, JsonSerializer, or JsonDeserializer
103
     *
104
     * @param TypeToken $type
105
     * @param JsonAdapter $jsonAdapterAnnotation
106
     * @return TypeAdapter
107
     * @throws InvalidArgumentException
108
     */
109 6
    public function getAdapterFromAnnotation(TypeToken $type, JsonAdapter $jsonAdapterAnnotation): TypeAdapter
110
    {
111 6
        $object = $this->constructorConstructor->get(TypeToken::create($jsonAdapterAnnotation->getValue()))->construct();
112
113 6
        if ($object instanceof TypeAdapter) {
114 1
            return $object;
115
        }
116
117 5
        if ($object instanceof TypeAdapterFactory) {
118 1
            return $object->create($type, $this);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $object->create($type, $this) could return the type null which is incompatible with the type-hinted return Tebru\Gson\TypeAdapter. Consider adding an additional type-check to rule them out.
Loading history...
119
        }
120
121 4
        if ($object instanceof JsonSerializer && $object instanceof JsonDeserializer) {
122 1
            return new CustomWrappedTypeAdapter($type, $this, $object, $object);
123
        }
124
125 3
        if ($object instanceof JsonSerializer) {
126 1
            return new CustomWrappedTypeAdapter($type, $this, $object);
127
        }
128
129 2
        if ($object instanceof JsonDeserializer) {
130 1
            return new CustomWrappedTypeAdapter($type, $this, null, $object);
131
        }
132
133 1
        throw new InvalidArgumentException(sprintf(
134 1
            'The type adapter must be an instance of TypeAdapter, TypeAdapterFactory, JsonSerializer, or JsonDeserializer, but "%s" was found',
135 1
            get_class($object)
136
        ));
137
    }
138
}
139