Completed
Push — master ( c973b9...2d3fae )
by Joschi
03:05
created

RdfaliteElementProcessor   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 224
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 77.92%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 32
c 5
b 0
f 0
lcom 1
cbo 6
dl 0
loc 224
ccs 60
cts 77
cp 0.7792
rs 9.6

9 Methods

Rating   Name   Duplication   Size   Complexity  
A processElement() 0 12 1
A processVocab() 0 9 2
A processPrefix() 0 13 3
B processProperty() 0 21 6
A getPropertyValue() 0 14 3
B getThing() 0 26 4
C getPropertyStringValue() 0 28 8
A processElementChildren() 0 5 1
A processTypeof() 0 20 4
1
<?php
2
3
/**
4
 * rdfa-lite
5
 *
6
 * @category Jkphl
7
 * @package Jkphl\Rdfalite
8
 * @subpackage Jkphl\Rdfalite\Infrastructure
9
 * @author Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright Copyright © 2017 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2017 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Jkphl\Rdfalite\Infrastructure\Parser;
38
39
use Jkphl\Rdfalite\Application\Contract\ElementProcessorInterface;
40
use Jkphl\Rdfalite\Application\Parser\Context;
41
use Jkphl\Rdfalite\Domain\Property\Property;
42
use Jkphl\Rdfalite\Domain\Thing\Thing;
43
use Jkphl\Rdfalite\Domain\Thing\ThingInterface;
44
use Jkphl\Rdfalite\Domain\Vocabulary\Vocabulary;
45
use Jkphl\Rdfalite\Domain\Vocabulary\VocabularyInterface;
46
use Jkphl\Rdfalite\Infrastructure\Exceptions\OutOfBoundsException;
47
48
/**
49
 * RDFa Lite 1.1 element processor
50
 *
51
 * @package Jkphl\Rdfalite
52
 * @subpackage Jkphl\Rdfalite\Infrastructure
53
 */
54
class RdfaliteElementProcessor implements ElementProcessorInterface
55
{
56
    /**
57
     * Process a DOM element
58
     *
59
     * @param \DOMElement $element DOM element
60
     * @param Context $context Inherited Context
61
     * @return Context Local context for this element
62
     */
63 2
    public function processElement(\DOMElement $element, Context $context)
64
    {
65
        // Process default vocabulary registrations
66 2
        $context = $this->processVocab($element, $context);
67
68
        // Register vocabulary prefixes
69 2
        $context = $this->processPrefix($element, $context);
70
71
        // Create properties
72 2
        $context = $this->processProperty($element, $context);
73 2
        return $context;
74
    }
75
76
    /**
77
     * Process changes of the default vocabulary
78
     *
79
     * @param \DOMElement $element DOM element
80
     * @param Context $context Inherited Context
81
     * @return Context Local context for this element
82
     */
83 2
    protected function processVocab(\DOMElement $element, Context $context)
84
    {
85 2
        if ($element->hasAttribute('vocab')) {
86 2
            $defaultVocabulary = new Vocabulary($element->getAttribute('vocab'));
87 2
            $context = $context->setDefaultVocabulary($defaultVocabulary);
88
        }
89
90 2
        return $context;
91
    }
92
93
    /**
94
     * Process vocabulary prefixes
95
     *
96
     * @param \DOMElement $element DOM element
97
     * @param Context $context Inherited Context
98
     * @return Context Local context for this element
99
     */
100 2
    protected function processPrefix(\DOMElement $element, Context $context)
101
    {
102 2
        if ($element->hasAttribute('prefix')) {
103 2
            $prefixes = preg_split('/\s+/', $element->getAttribute('prefix'));
104 2
            while (count($prefixes)) {
105 2
                $prefix = rtrim(array_shift($prefixes), ':');
106 2
                $uri = array_shift($prefixes);
107 2
                $context = $context->registerVocabulary($prefix, $uri);
108
            }
109
        }
110
111 2
        return $context;
112
    }
113
114
    /**
115
     * Create properties
116
     *
117
     * @param \DOMElement $element DOM element
118
     * @param Context $context Inherited Context
119
     * @return Context Local context for this element
120
     */
121 2
    protected function processProperty(\DOMElement $element, Context $context)
122
    {
123 2
        if ($element->hasAttribute('property') && ($context->getParentThing() instanceof ThingInterface)) {
124 2
            $property = explode(':', $element->getAttribute('property'));
125 2
            $name = strval(array_pop($property));
126 2
            $prefix = strval(array_pop($property));
127
128
            // Determine the vocabulary to use
129 2
            $vocabulary = empty($prefix) ? $context->getDefaultVocabulary() : $context->getVocabulary($prefix);
130 2
            if ($vocabulary instanceof VocabularyInterface) {
131
                // Try to get a resource ID
132 2
                $resourceId = trim($element->getAttribute('resource')) ?: null;
133
134
                // Add the property to the current parent thing
135 2
                $property = new Property($name, $vocabulary, $this->getPropertyValue($element, $context), $resourceId);
136 2
                $context->getParentThing()->addProperty($property);
137
            }
138
        }
139
140 2
        return $context;
141
    }
142
143
    /**
144
     * Return a property value (type and tag name dependent)
145
     *
146
     * @param \DOMElement $element DOM element
147
     * @param Context $context Context
148
     * @return ThingInterface|string Property value
149
     */
150 2
    protected function getPropertyValue(\DOMElement $element, Context $context)
151
    {
152
        // If the property creates a new type: Return the element itself
153 2
        if ($element->hasAttribute('typeof')) {
154
            return $this->getThing(
155
                $element->getAttribute('typeof'),
156
                trim($element->getAttribute('resource')) ?: null,
157
                $context
158
            );
159
        }
160
161
        // Return a string property value
162 2
        return $this->getPropertyStringValue($element);
163
    }
164
165
    /**
166
     * Return a property value (type and tag name dependent)
167
     *
168
     * @param string $typeof Thing type
169
     * @param string $resourceId Resource ID
170
     * @param Context $context Context
171
     * @return ThingInterface Thing
172
     * @throws OutOfBoundsException If the default vocabulary is empty
173
     * @throws OutOfBoundsException If the vocabulary prefix is unknown
174
     */
175 2
    protected function getThing($typeof, $resourceId, Context $context)
176
    {
177 2
        $typeof = explode(':', $typeof);
178 2
        $type = array_pop($typeof);
179 2
        $prefix = array_pop($typeof);
180
181
        // Determine the vocabulary to use
182 2
        $vocabulary = empty($prefix) ? $context->getDefaultVocabulary() : $context->getVocabulary($prefix);
183 2
        if ($vocabulary instanceof VocabularyInterface) {
184
            // Return a new thing
185 2
            return new Thing($type, $vocabulary, $resourceId);
186
        }
187
188
        // If the default vocabulary is empty
189
        if (empty($prefix)) {
190
            throw new OutOfBoundsException(
191
                OutOfBoundsException::EMPTY_DEFAULT_VOCABULARY_STR,
192
                OutOfBoundsException::EMPTY_DEFAULT_VOCABULARY
193
            );
194
        }
195
196
        throw new OutOfBoundsException(
197
            sprintf(OutOfBoundsException::UNKNOWN_VOCABULARY_PREFIX_STR, $prefix),
198
            OutOfBoundsException::UNKNOWN_VOCABULARY_PREFIX
199
        );
200
    }
201
202
    /**
203
     * Return a property value (type and tag name dependent)
204
     *
205
     * @param \DOMElement $element DOM element
206
     * @return ThingInterface|string Property value
207
     */
208 2
    protected function getPropertyStringValue(\DOMElement $element)
209
    {
210 2
        $tagName = strtoupper($element->tagName);
211
212
        // Else: Depend on the tag name
213
        switch (true) {
214 2
            case $tagName === 'META':
215
                return strval($element->getAttribute('content'));
216 2
            case in_array($tagName, ['AUDIO', 'EMBED', 'IFRAME', 'IMG', 'SOURCE', 'TRACK', 'VIDEO']):
217 2
                return strval($element->getAttribute('src'));
218
219 2
            case in_array($tagName, ['A', 'AREA', 'LINK']):
220
                return strval($element->getAttribute('href'));
221 2
            case $tagName === 'OBJECT':
222
                return strval($element->getAttribute('data'));
223 2
            case $tagName === 'DATA':
224
                return strval($element->getAttribute('value'));
225 2
            case $tagName === 'TIME':
226
                $datetime = $element->getAttribute('datetime');
227
                if (!empty($datetime)) {
228
                    return strval($datetime);
229
                }
230
            // fall through
231
            default:
232
//              trigger_error(sprintf('RDFa Lite 1.1 element processor: Unhandled tag name "%s"', $element->tagName), E_USER_WARNING);
233 2
                return $element->textContent;
234
        }
235
    }
236
237
    /**
238
     * Process a DOM element's children
239
     *
240
     * @param \DOMElement $element DOM element
241
     * @param Context $context Context
242
     * @return Context Context for children
243
     */
244 2
    public function processElementChildren(\DOMElement $element, Context $context)
245
    {
246
        // Process nested children
247 2
        return $this->processTypeof($element, $context);
248
    }
249
250
    /**
251
     * Create nested children
252
     *
253
     * @param \DOMElement $element DOM element
254
     * @param Context $context Context
255
     * @return Context Context for children
256
     */
257 2
    protected function processTypeof(\DOMElement $element, Context $context)
258
    {
259 2
        if ($element->hasAttribute('typeof')) {
260 2
            $thing = $this->getThing(
261 2
                $element->getAttribute('typeof'),
262 2
                trim($element->getAttribute('resource')) ?: null,
263
                $context
264
            );
265
266 2
            if ($thing instanceof ThingInterface) {
267
                // Add the new thing as a child to the current context
268 2
                $context->addChild($thing);
269
270
                // Set the thing as parent thing for nested iterations
271 2
                $context = $context->setParentThing($thing);
272
            }
273
        }
274
275 2
        return $context;
276
    }
277
}
278