Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
ImsManifestMetadataExtractor
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
1 / 1
17
100.00% covered (success)
100.00%
1 / 1
 extract
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
1 / 1
17
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) 2014 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22namespace oat\taoQtiItem\model\qti\metadata\imsManifest;
23
24use DOMDocument;
25use DOMXPath;
26use DOMText;
27use oat\taoQtiItem\model\qti\metadata\MetadataExtractionException;
28use oat\taoQtiItem\model\qti\metadata\MetadataExtractor;
29
30/**
31 * A MetadataExtractor implementation
32 * This implementation simply iterate through nodes and create an array of MetadataSimpleInstance object
33 *
34 * @author Antoine Robin <antoine.robin@vesperiagroup.com>
35 */
36class ImsManifestMetadataExtractor implements MetadataExtractor
37{
38    /**
39     * @see MetadataExtractor::extract()
40     */
41    public function extract($manifest)
42    {
43        if ($manifest instanceof DOMDocument) {
44            $bases = [];
45
46            // get the base for paths.
47            $xpath = new DOMXPath($manifest);
48            foreach ($xpath->query('namespace::*', $manifest->ownerDocument) as $node) {
49                $bases[str_replace('xmlns:', '', $node->nodeName)] = $node->nodeValue;
50            }
51
52            $manifestElt = $manifest->documentElement;
53            $rootNs = $manifestElt->namespaceURI;
54
55            // Extract metadata on a <resource basis>.
56            $xpath->registerNamespace('man', $rootNs);
57
58            // Prepare data structure to be returned.
59            $metadata = [];
60
61            $resourcesElt = $xpath->query('/man:manifest/man:resources/man:resource');
62            foreach ($resourcesElt as $resourceElt) {
63                $identifier = $resourceElt->getAttribute('identifier');
64                $href = $resourceElt->getAttribute('href');
65                $type = $resourceElt->getAttribute('type');
66
67                $metadataElts = $xpath->query('man:metadata', $resourceElt);
68                foreach ($metadataElts as $metadataElt) {
69                    // Ask for metadata domains.
70                    $domainElts = $xpath->query(
71                        '*[not(self::man:schema) and not(self::man:schemaversion)]',
72                        $metadataElt
73                    );
74
75                    foreach ($domainElts as $domainElt) {
76                        $trail = [];
77                        $visited = [];
78                        $path = [];
79                        $parent = null;
80
81                        array_push($trail, $domainElt);
82
83                        while (count($trail) > 0) {
84                            $currentElt = array_pop($trail);
85
86                            if (!$currentElt instanceof DOMText && in_array($currentElt, $visited, true) === false) {
87                                // Hierarchical node, 1st visit.
88
89                                // Push current for a future ascending exploration.
90                                array_push($trail, $currentElt);
91
92                                // Push children on the trail for descending exploration.
93                                $nodesToExplore = $currentElt->childNodes;
94
95                                if ($nodesToExplore) {
96                                    for ($i = ($nodesToExplore->length - 1); $i >= 0; $i--) {
97                                        array_push($trail, $nodesToExplore->item($i));
98                                    }
99                                }
100
101                                // Set current as visited.
102                                array_push($visited, $currentElt);
103
104                                // Update the path.
105                                array_push($path, $currentElt->namespaceURI . '#' . $currentElt->localName);
106
107                                // Reference parent for leaf nodes.
108                                $parent = $currentElt;
109                            } elseif ($currentElt instanceof DOMText && ctype_space($currentElt->wholeText) === false) {
110                                // Leaf node, 1st and only visit.
111                                $metadataValue = new ImsManifestMetadataValue(
112                                    $identifier,
113                                    $type,
114                                    $href,
115                                    $path,
116                                    $currentElt->wholeText
117                                );
118
119                                if ($parent !== null && $parent->hasAttributeNS($bases['xml'], 'lang')) {
120                                    $metadataValue->setLanguage($parent->getAttributeNS($bases['xml'], 'lang'));
121                                }
122
123                                if (isset($metadata[$identifier]) === false) {
124                                    $metadata[$identifier] = [];
125                                }
126
127                                $metadata[$identifier][] = $metadataValue;
128                            } elseif (in_array($currentElt, $visited, true) === true) {
129                                // Hierarchical node, second visit (ascending).
130
131                                // Update the path.
132                                array_pop($path);
133                            }
134                        }
135                    }
136                }
137            }
138
139            return $metadata;
140        } else {
141            throw new MetadataExtractionException(__('The manifest argument must be an instance of DOMDocument.'));
142        }
143    }
144}