Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 73
0.00% covered (danger)
0.00%
0 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractMetadataService
0.00% covered (danger)
0.00%
0 / 73
0.00% covered (danger)
0.00%
0 / 15
1722
0.00% covered (danger)
0.00%
0 / 1
 registerMetadataService
n/a
0 / 0
n/a
0 / 0
0
 extract
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 inject
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 register
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
56
 unregister
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
 getMetadataValues
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMetadataValues
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExtractors
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 registerInstance
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 unregisterInstance
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 hasMetadataValue
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMetadataValue
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getInjectors
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInstances
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 getInstance
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getConfigurableServiceKey
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2017 (original work) Open Assessment Technologies SA;
19 *
20 */
21
22namespace oat\taoQtiItem\model\qti\metadata;
23
24use oat\oatbox\service\ConfigurableService;
25
26/**
27 * Class AbstractMetadataService
28 * Base class for a metadata Service e.q. MetadataExporter or MetadataImporter
29 *
30 * @package oat\taoQtiItem\model\qti\metadata
31 * @author Moyon Camille
32 */
33abstract class AbstractMetadataService extends ConfigurableService
34{
35    /**
36     * Config key to store injectors classes
37     */
38    public const INJECTOR_KEY  = 'injectors';
39
40    /**
41     * Config key to store extractors classes
42     */
43    public const EXTRACTOR_KEY = 'extractors';
44
45    /**
46     * @var array Array of metadata, with metadata key with associated value
47     */
48    protected $metadataValues;
49
50    /**
51     * @var array Instances to manage metadata values
52     */
53    protected $instances;
54
55    /**
56     * Allow to register into config a metadataService
57     */
58    abstract protected function registerMetadataService();
59
60    /**
61     * Extract metadata value of a given $source
62     *
63     * Extract metadata values by calling each extractors
64     *
65     * @param $source
66     * @return array
67     */
68    public function extract($source)
69    {
70        $metadata = [[]];
71        foreach ($this->getExtractors() as $extractor) {
72            $metadata[] = $extractor->extract($source);
73        }
74        $metadata = array_merge_recursive(...$metadata);
75        \common_Logger::d(__('%s metadata values found in source by extractor(s).', count($metadata)));
76
77        return $metadata;
78    }
79
80    /**
81     * Inject metadata value for an identifier through injectors
82     *
83     * Inject metadata value for an identifier by calling each injectors
84     * Injectors need $target for injection
85     *
86     * @param $identifier
87     * @param $target
88     */
89    public function inject($identifier, $target)
90    {
91        if ($this->hasMetadataValue($identifier)) {
92            \common_Logger::i(__('Preparing Metadata Values for target "%s"...', $identifier));
93            $values = $this->getMetadataValue($identifier);
94
95            foreach ($this->getInjectors() as $injector) {
96                \common_Logger::i(
97                    // phpcs:disable Generic.Files.LineLength
98                    __('Attempting to inject "%s" metadata values for target "%s" with metadata Injector "%s".', count($values), $identifier, get_class($injector))
99                    // phpcs:enable Generic.Files.LineLength
100                );
101                $injector->inject($target, [$identifier => $values]);
102            }
103        }
104    }
105
106    /**
107     * Register an importer instance
108     *
109     * Register an instance e.q. Injectors, Extractors, Guardians or LooUpClass
110     * Respective interface is checked
111     * Throw exception if call if not correctly formed
112     *
113     * @param $key
114     * @param $name
115     * @return bool
116     * @throws \InvalidArgumentException
117     * @throws \common_Exception
118     */
119    public function register($key, $name)
120    {
121        if (empty($key) || empty($name)) {
122            throw new \InvalidArgumentException(__('Register method expects $key and $name parameters'));
123        }
124
125        if (is_object($name)) {
126            $name = get_class($name);
127        }
128
129        switch ($key) {
130            case self::EXTRACTOR_KEY:
131                $this->registerInstance(self::EXTRACTOR_KEY, $name, MetadataExtractor::class);
132                break;
133            case self::INJECTOR_KEY:
134                $this->registerInstance(self::INJECTOR_KEY, $name, MetadataInjector::class);
135                break;
136            default:
137                throw new \common_Exception(__('Unknown $key to register MetadataService instance'));
138        }
139
140        return true;
141    }
142
143    /**
144     * Unregister a instance of metadataService config
145     *
146     * Unregister an instance with helper method unregisterInstance
147     *
148     * @param string $key The config key where unregister instance
149     * @param string $name The instance name to unregister
150     * @throws \common_Exception If method is call with empty argument
151     */
152    public function unregister($key, $name)
153    {
154        if (empty($key) || empty($name)) {
155            throw new \common_Exception();
156        }
157
158        if (is_object($name)) {
159            $name = get_class($name);
160        }
161
162        switch ($key) {
163            case self::EXTRACTOR_KEY:
164                $this->unregisterInstance(self::EXTRACTOR_KEY, $name);
165                break;
166            case self::INJECTOR_KEY:
167                $this->unregisterInstance(self::INJECTOR_KEY, $name);
168                break;
169        }
170        return;
171    }
172
173    /**
174     * Return metadata values
175     *
176     * @return array
177     */
178    public function getMetadataValues()
179    {
180        return $this->metadataValues;
181    }
182
183    /**
184     * Set metadata values with given array
185     *
186     * @param array $metadataValues
187     */
188    public function setMetadataValues(array $metadataValues)
189    {
190        $this->metadataValues = $metadataValues;
191    }
192
193    /**
194     *
195     * @return MetadataExtractor[]
196     */
197    public function getExtractors()
198    {
199        return $this->getInstances(self::EXTRACTOR_KEY, MetadataExtractor::class);
200    }
201
202    /**
203     * Register an instance
204     *
205     * Register a $name instance into $key config
206     * $key class has to implements $interface
207     *
208     * @param $key
209     * @param $name
210     * @param $interface
211     */
212    protected function registerInstance($key, $name, $interface)
213    {
214        if (is_a($name, $interface, true)) {
215            $instances = $this->getOption($key);
216            if ($instances === null || array_search($name, $instances) === false) {
217                $instances[] = $name;
218                $this->setOption($key, $instances);
219                $this->registerMetadataService();
220            }
221        }
222    }
223
224    /**
225     * Unregister an instance
226     *
227     * Unregister a $name instance into $key config
228     *
229     * @param $key
230     * @param $name
231     */
232    protected function unregisterInstance($key, $name)
233    {
234        if ($this->hasOption($key)) {
235            $instances = $this->getOption($key);
236
237            if (($index = array_search($name, $instances)) !== false) {
238                unset($instances[$index]);
239                $this->setOption($key, $instances);
240                $this->registerMetadataService();
241            }
242        }
243    }
244
245    /**
246     * Check if metadata value $identifier exists
247     *
248     * @param $identifier
249     * @return bool
250     */
251    public function hasMetadataValue($identifier)
252    {
253        return array_key_exists($identifier, $this->getMetadataValues());
254    }
255
256    /**
257     * Return the associated value of metadata $identifier or null if not exists
258     *
259     * @param $identifier
260     * @return mixed|null
261     */
262    public function getMetadataValue($identifier)
263    {
264        $metadata = $this->getMetadataValues();
265        return isset($metadata[$identifier]) ? $metadata[$identifier] : null;
266    }
267
268    /**
269     *
270     * @return MetadataInjector[]
271     */
272    protected function getInjectors()
273    {
274        return $this->getInstances(self::INJECTOR_KEY, MetadataInjector::class);
275    }
276
277    /**
278     * Return config instances
279     *
280     * Retrieve instances stored into config
281     * Config $key is scan to take only instances with given $interface
282     *
283     * @param $id
284     * @param $interface
285     * @return mixed
286     */
287    protected function getInstances($id, $interface)
288    {
289        if (isset($this->instances[$id])) {
290            return $this->instances[$id];
291        }
292
293        $this->instances[$id] = [];
294
295        if (! $this->hasOption($id)) {
296            return $this->instances[$id];
297        } else {
298            foreach ($this->getOption($id) as $instance) {
299                if (is_a($instance, $interface, true)) {
300                    $this->instances[$id][] = $this->getInstance($instance);
301                }
302            }
303        }
304
305        return $this->instances[$id];
306    }
307
308    private function getInstance(string $instance): object
309    {
310        $isConfigurableService = is_a($instance, ConfigurableService::class, true);
311
312        if ($isConfigurableService) {
313            return $this->getServiceLocator()->get($this->getConfigurableServiceKey($instance));
314        }
315
316        return new $instance();
317    }
318
319    private function getConfigurableServiceKey(string $instance): string
320    {
321        return defined($instance . '::SERVICE_ID')
322            ? $instance::SERVICE_ID
323            : $instance;
324    }
325}