Completed
Push — master ( fdd253...65cf44 )
by Henri
04:31
created

Vocabulary::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 5
rs 9.4286
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
/**
3
 * Copyright (c) 2012-2013 Aalto University and University of Helsinki
4
 * MIT License
5
 * see LICENSE.txt for more information
6
 */
7
8
/**
9
 * Vocabulary dataobjects provide access to the vocabularies on the SPARQL endpoint.
10
 */
11
class Vocabulary extends DataObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
12
{
13
    /** cached value of URI space */
14
    private $urispace = null;
15
    private $config;
16
17
    public function __construct($model, $resource)
18
    {
19
        parent::__construct($model, $resource);
20
        $this->config = new VocabularyConfig($resource);
21
    }
22
23
    /**
24
     * Returns the VocabularyConfig object
25
     * @return VocabularyConfig
26
     */
27
    public function getConfig()
28
    {
29
      return $this->config;
30
    }
31
32
    /**
33
     * Get the SPARQL endpoint URL for this vocabulary
34
     *
35
     * @return string endpoint URL
36
     */
37
    public function getEndpoint()
38
    {
39
        return $this->resource->get('void:sparqlEndpoint')->getUri();
40
    }
41
42
    /**
43
     * Get the SPARQL graph URI for this vocabulary
44
     *
45
     * @return string graph URI
46
     */
47
    public function getGraph()
48
    {
49
        $graph = $this->resource->get('skosmos:sparqlGraph');
50
        if ($graph) {
51
            $graph = $graph->getUri();
52
        }
53
54
        return $graph;
55
    }
56
57
    /**
58
     * Get the SPARQL implementation for this vocabulary
59
     *
60
     * @return Sparql SPARQL object
61
     */
62
    public function getSparql()
63
    {
64
        $endpoint = $this->getEndpoint();
65
        $graph = $this->getGraph();
66
        $dialect = $this->resource->get('skosmos:sparqlDialect');
67
        $dialect = $dialect ? $dialect->getValue() : DEFAULT_SPARQL_DIALECT;
68
69
        return $this->model->getSparqlImplementation($dialect, $endpoint, $graph);
70
    }
71
72
    /**
73
     * Get the URI space of concepts in this vocabulary.
74
     *
75
     * @return string full URI of concept
76
     */
77
    public function getUriSpace()
78
    {
79
        if ($this->urispace === null) // initialize cache
80
        {
81
            $this->urispace = $this->resource->getLiteral('void:uriSpace')->getValue();
82
        }
83
84
        return $this->urispace;
85
    }
86
87
    /**
88
     * Get the full URI of a concept in a vocabulary. If the passed local
89
     * name is already a full URI, return it unchanged.
90
     *
91
     * @param $lname string local name of concept
92
     * @return string full URI of concept
93
     */
94
    public function getConceptURI($lname)
95
    {
96
        if (strpos($lname, 'http') === 0) {
97
            return $lname;
98
        }
99
        // already a full URI
100
        return $this->getUriSpace() . $lname;
101
    }
102
103
    /**
104
     * Asks the sparql implementation to make a label query for a uri.
105
     * @param string $uri
106
     * @param string $lang
107
     */
108
    public function getConceptLabel($uri, $lang)
109
    {
110
        return $this->getSparql()->queryLabel($uri, $lang);
111
    }
112
113
    /**
114
     * Get the localname of a concept in the vocabulary. If the URI is not
115
     * in the URI space of this vocabulary, return the full URI.
116
     *
117
     * @param $uri string full URI of concept
118
     * @return string local name of concept, or original full URI if the local name cannot be determined
119
     */
120
    public function getLocalName($uri)
121
    {
122
        return str_replace($this->getUriSpace(), "", $uri);
123
    }
124
125
    /**
126
     * Retrieves all the information about the Vocabulary
127
     * from the SPARQL-endpoint.
128
     */
129
    public function getInfo($lang = null)
130
    {
131
        $ret = array();
132
        if (!$lang) {
133
            $lang = $this->getEnvLang();
134
        }
135
136
        // get metadata from vocabulary configuration file
137
        foreach ($this->resource->properties() as $prop) {
138
            foreach ($this->resource->allLiterals($prop, $lang) as $val) {
139
                $ret[$prop][] = $val->getValue();
140
            }
141
            foreach ($this->resource->allResources($prop) as $val) {
142
                $label = $val->label($lang);
143
                if ($label) {
144
                    $ret[$prop][] = $label->getValue();
145
                }
146
            }
147
        }
148
149
        // also include ConceptScheme metadata from SPARQL endpoint
150
        $defaultcs = $this->getDefaultConceptScheme();
151
152
        // query everything the endpoint knows about the ConceptScheme
153
        $sparql = $this->getSparql();
154
        $result = $sparql->queryConceptScheme($defaultcs);
155
        $conceptscheme = $result->resource($defaultcs);
156
        $this->order = array("dc:title", "dc11:title", "skos:prefLabel", "rdfs:label", "dc:subject", "dc11:subject", "dc:description", "dc11:description", "dc:publisher", "dc11:publisher", "dc:creator", "dc11:creator", "dc:contributor", "dc:language", "dc11:language", "owl:versionInfo", "dc:source", "dc11:source");
157
158
        foreach ($conceptscheme->properties() as $prop) {
159
            foreach ($conceptscheme->allLiterals($prop, $lang) as $val) {
160
                $ret[$prop][] = $val;
161
            }
162
            if (!isset($ret[$prop]) || sizeof($ret[$prop]) == 0) { // not found with language tag
163
                foreach ($conceptscheme->allLiterals($prop, null) as $val) {
164
                    $value = $val->getValue();
165
                    if ($value instanceof DateTime) {
166
                        $val = Punic\Calendar::formatDate($value, 'full', $lang) . ' ' . Punic\Calendar::format($value, 'HH:mm:ss', $lang);
167
                    }
168
                    $ret[$prop][] = $val;
169
                }
170
            }
171
            foreach ($conceptscheme->allResources($prop) as $val) {
172
                $exvocab = $this->model->guessVocabularyFromURI($val->getURI());
173
                $exlabel = $this->getExternalLabel($exvocab, $val->getURI(), $lang);
174
                if (isset($exlabel)) {
175
                    $val->add('skosmos:vocab', $exvocab->getId());
176
                    $val->add('skosmos:label', $exlabel);
177
                }
178
                $ret[$prop][] = $val;
179
            }
180
        }
181
        if (isset($ret['owl:versionInfo'])) { // if version info availible for vocabulary convert it to a more readable format
182
            $ret['owl:versionInfo'][0] = $this->parseVersionInfo($ret['owl:versionInfo'][0]);
183
        }
184
        // remove duplicate values
185
        foreach (array_keys($ret) as $prop) {
186
            $ret[$prop] = array_unique($ret[$prop]);
187
        }
188
189
        $ret = $this->arbitrarySort($ret);
190
191
        // filtering multiple labels
192
        if (isset($ret['dc:title'])) {
193
            unset($ret['dc11:title'], $ret['skos:prefLabel'], $ret['rdfs:label']);
194
        } else if (isset($ret['dc11:title'])) {
195
            unset($ret['skos:prefLabel'], $ret['rdfs:label']);
196
        } else if (isset($ret['skos:prefLabel'])) {
197
            unset($ret['rdfs:label']);
198
        }
199
200
        return $ret;
201
    }
202
203
    /**
204
     * Return all concept schemes in the vocabulary.
205
     * @return array Array with concept scheme URIs (string) as keys and labels (string) as values
206
     */
207
208
    public function getConceptSchemes($lang = '')
209
    {
210
        if ($lang === '') {
211
            $lang = $this->getEnvLang();
212
        }
213
214
        return $this->getSparql()->queryConceptSchemes($lang);
215
    }
216
217
    /**
218
     * Return the URI of the default concept scheme of this vocabulary. If the skosmos:mainConceptScheme property is set in the
219
     * vocabulary configuration, that will be returned. Otherwise an arbitrary concept scheme will be returned.
220
     * @return string concept scheme URI
221
     */
222
223
    public function getDefaultConceptScheme()
224
    {
225
        $conceptScheme = $this->resource->get("skosmos:mainConceptScheme");
226
        if ($conceptScheme) {
227
            return $conceptScheme->getUri();
228
        }
229
230
        // mainConceptScheme not explicitly set, guess it
231
        foreach ($this->getConceptSchemes() as $uri => $csdata) {
232
            $conceptScheme = $uri; // actually pick the last one
233
        }
234
235
        return $conceptScheme;
236
    }
237
238
    /**
239
     * Return the top concepts of a concept scheme in the vocabulary.
240
     * @param string $conceptScheme URI of concept scheme whose top concepts to return. If not set,
241
     *                              the default concept scheme of the vocabulary will be used.
242
     * @param string $lang preferred language for the concept labels,
243
     * @return array Array with concept URIs (string) as keys and labels (string) as values
244
     */
245
246
    public function getTopConcepts($conceptScheme = null, $lang = '')
247
    {
248
        if ($lang === '') {
249
            $lang = $this->getEnvLang();
250
        }
251
252
        if ($conceptScheme === null || $conceptScheme == '') {
253
            $conceptScheme = $this->getDefaultConceptScheme();
254
        }
255
256
        return $this->getSparql()->queryTopConcepts($conceptScheme, $lang);
257
    }
258
259
    /**
260
     * Tries to parse version, date and time from sparql version information into a readable format.
261
     * @param string $version
262
     * @return string
263
     */
264
    private function parseVersionInfo($version)
265
    {
266
        $parts = explode(' ', $version);
267
        if ($parts[0] != '$Id:') {
268
            return $version;
269
        }
270
        // don't know how to parse
271
        $rev = $parts[2];
272
        $datestr = $parts[3] . ' ' . $parts[4];
273
274
        return "$datestr (r$rev)";
275
    }
276
277
    /**
278
     * Counts the statistics of the vocabulary.
279
     * @return array of the concept counts in different languages
280
     */
281
    public function getStatistics($lang = '')
282
    {
283
        $sparql = $this->getSparql();
284
        // find the number of concepts
285
        return $sparql->countConcepts($lang);
286
    }
287
288
    /**
289
     * Counts the statistics of the vocabulary.
290
     * @return array of the concept counts in different languages
291
     */
292
    public function getLabelStatistics()
293
    {
294
        $sparql = $this->getSparql();
295
        $ret = array();
296
        // count the number of different types of concepts in all languages
297
        $ret['terms'] = $sparql->countLangConcepts($this->config->getLanguages(), $this->config->getIndexClasses());
298
299
        return $ret;
300
    }
301
302
    /**
303
     * Gets the parent concepts of a concept and child concepts for all of those.
304
     * @param string $uri
305
     * @param string $lang language identifier.
306
     */
307
    public function getConceptHierarchy($uri, $lang)
308
    {
309
        $lang = $lang ? $lang : $this->getEnvLang();
310
        $fallback = $this->config->getDefaultLanguage();
311
        return $this->getSparql()->queryParentList($uri, $lang, $fallback);
312
    }
313
314
    /**
315
     * Gets the child relations of a concept and whether these children have more children.
316
     * @param string $uri
317
     */
318
    public function getConceptChildren($uri, $lang)
319
    {
320
        $lang = $lang ? $lang : $this->getEnvLang();
321
        $fallback = $this->config->getDefaultLanguage();
322
        return $this->getSparql()->queryChildren($uri, $lang, $fallback);
323
    }
324
325
    /**
326
     * Gets the skos:narrower relations of a concept.
327
     * @param string $uri
328
     * @param string $lang language identifier.
329
     */
330
    public function getConceptNarrowers($uri, $lang)
331
    {
332
        $lang = $lang ? $lang : $this->getEnvLang();
333
        return $this->getSparql()->queryProperty($uri, 'skos:narrower', $lang);
334
    }
335
336
    /**
337
     * Gets the skos:narrowerTransitive relations of a concept.
338
     * @param string $uri
339
     * @param integer $limit
340
     * @param string $lang language identifier.
341
     */
342
    public function getConceptTransitiveNarrowers($uri, $limit, $lang)
343
    {
344
        $lang = $lang ? $lang : $this->getEnvLang();
345
        return $this->getSparql()->queryTransitiveProperty($uri, 'skos:narrower', $lang, $limit);
346
    }
347
348
    /**
349
     * Gets the skos:broader relations of a concept.
350
     * @param string $uri
351
     * @param string $lang language identifier.
352
     */
353
    public function getConceptBroaders($uri, $lang)
354
    {
355
        $lang = $lang ? $lang : $this->getEnvLang();
356
        return $this->getSparql()->queryProperty($uri, 'skos:broader', $lang);
357
    }
358
359
    /**
360
     * Gets the skos:broaderTransitive relations of a concept.
361
     * @param string $uri
362
     * @param integer $limit
363
     * @param boolean $any set to true if you want to have a label even in case of a correct language one missing.
364
     * @param string $lang language identifier.
365
     */
366
    public function getConceptTransitiveBroaders($uri, $limit, $any = false, $lang)
367
    {
368
        $lang = $lang ? $lang : $this->getEnvLang();
369
        $fallback = $this->config->getDefaultLanguage();
370
        return $this->getSparql()->queryTransitiveProperty($uri, 'skos:broader', $lang, $limit, $any, $fallback);
371
    }
372
373
    /**
374
     * Gets all the skos:related concepts of a concept.
375
     * @param string $uri
376
     * @param string $lang language identifier.
377
     */
378
    public function getConceptRelateds($uri, $lang)
379
    {
380
        $lang = $lang ? $lang : $this->getEnvLang();
381
        return $this->getSparql()->queryProperty($uri, 'skos:related', $lang);
382
    }
383
384
    /**
385
     * Makes a query into the sparql endpoint for a concept.
386
     * @param string $uri the full URI of the concept
387
     * @return array
388
     */
389
    public function getConceptInfo($uri, $clang)
390
    {
391
        $sparql = $this->getSparql();
392
393
        return $sparql->queryConceptInfo($uri, $this->config->getArrayClassURI(), array($this), null, $clang);
394
    }
395
396
    /**
397
     * Lists the different concept groups available in the vocabulary.
398
     * @param string $clang content language parameter
399
     * @return array
400
     */
401
    public function listConceptGroups($clang = null)
402
    {
403
        if ($clang === null || $clang == '') {
404
            $clang = $this->getEnvLang();
405
        }
406
407
        $ret = array();
408
        $gclass = $this->config->getGroupClassURI();
409
        if ($gclass === null) {
410
            return $ret;
411
        }
412
        // no group class defined, so empty result
413
        $groups = $this->getSparql()->listConceptGroups($gclass, $clang);
414
        foreach ($groups as $uri => $label) {
415
            $ret[$uri] = $label;
416
        }
417
418
        return $ret;
419
    }
420
421
    /**
422
     * Lists the concepts available in the concept group.
423
     * @param $clname
424
     * @return array
425
     */
426
    public function listConceptGroupContents($glname, $clang)
427
    {
428
        if (!$clang) {
429
            $clang = $this->config->getEnvLang();
430
        }
431
432
        $ret = array();
433
        $gclass = $this->config->getGroupClassURI();
434
        if ($gclass === null) {
435
            return $ret;
436
        }
437
        // no group class defined, so empty result
438
        $group = $this->getConceptURI($glname);
439
        $contents = $this->getSparql()->listConceptGroupContents($gclass, $group, $clang);
440
        foreach ($contents as $uri => $label) {
441
            $ret[$uri] = $label;
442
        }
443
444
        return $ret;
445
    }
446
447
    /**
448
     * Returns the letters of the alphabet which have been used in this vocabulary.
449
     * The returned letters may also include specials such as '0-9' (digits) and '!*' (special characters).
450
     * @param $clang content language
451
     * @return array array of letters
452
     */
453
    public function getAlphabet($clang)
454
    {
455
        $chars = $this->getSparql()->queryFirstCharacters($clang, $this->config->getIndexClasses());
456
        $letters = array();
457
        $digits = false;
458
        $specials = false;
459
        foreach ($chars as $char) {
460
            if (preg_match('/\p{L}/u', $char)) {
461
                $letters[] = $char;
462
            } elseif (preg_match('/\d/u', $char)) {
463
                $digits = true;
464
            } else {
465
                $specials = true;
466
            }
467
        }
468
        usort($letters, 'strcoll');
469
        if ($specials) {
470
            $letters[] = '!*';
471
        }
472
473
        if ($digits) {
474
            $letters[] = '0-9';
475
        }
476
477
        return $letters;
478
    }
479
480
    /**
481
     * Searches for concepts with a label starting with the specified letter.
482
     * Also the special tokens '0-9' (digits), '!*' (special characters) and '*'
483
     * (everything) are supported.
484
     * @param $letter letter (or special token) to search for
485
     */
486
    public function searchConceptsAlphabetical($letter, $limit = null, $offset = null, $clang = null)
487
    {
488
        return $this->getSparql()->queryConceptsAlphabetical($letter, $clang, $limit, $offset, $this->config->getIndexClasses());
489
    }
490
491
    /**
492
     * Makes a query for the transitive broaders of a concept and returns the concepts hierarchy processed for the view.
493
     * @param string $lang
494
     * @param string $uri
495
     */
496
    public function getBreadCrumbs($lang, $uri)
497
    {
498
        $broaders = $this->getConceptTransitiveBroaders($uri, 1000, true, $lang);
499
        $origCrumbs = $this->getCrumbs($broaders, $uri);
500
        return $this->combineCrumbs($origCrumbs);
501
    }
502
503
    /**
504
     * Takes the crumbs as a parameter and combines the crumbs if the path they form is too long.
505
     * @return array
506
     */
507
    private function combineCrumbs($origCrumbs)
508
    {
509
        $combined = array();
510
        foreach ($origCrumbs as $pathKey => $path) {
511
            $firstToCombine = true;
512
            $combinedPath = array();
513
            foreach ($path as $crumbKey => $crumb) {
514
                if ($crumb->getPrefLabel() === '...') {
515
                    array_push($combinedPath, $crumb);
516
                    if ($firstToCombine) {
517
                        $firstToCombine = false;
518
                    } else {
519
                        unset($origCrumbs[$pathKey][$crumbKey]);
520
                    }
521
                }
522
            }
523
            $combined[] = $combinedPath;
524
        }
525
526
        return array('combined' => $combined, 'breadcrumbs' => $origCrumbs);
527
    }
528
529
    /**
530
     * Recursive function for building the breadcrumb paths for the view.
531
     * @param array $bT contains the results of the broaderTransitive query.
532
     * @param string $uri
533
     * @param array $path
534
     */
535
    private function getCrumbs($bT, $uri, $path = null)
536
    {
537
        $crumbs = array();
538
        if (!isset($path)) {
539
            $path = array();
540
        }
541
542
        // check that there is no cycle (issue #220)
543
        foreach ($path as $childcrumb) {
544
            if ($childcrumb->getUri() == $uri) {
545
                // found a cycle - short-circuit and stop
546
                return $crumbs;
547
            }
548
        }
549
        if (isset($bT[$uri]['direct'])) {
550
            foreach ($bT[$uri]['direct'] as $broaderUri) {
551
                $newpath = array_merge($path, array(new Breadcrumb($uri, $bT[$uri]['label'])));
552
                if ($uri !== $broaderUri) {
553
                    $crumbs = array_merge($crumbs, $this->getCrumbs($bT, $broaderUri, $newpath));
554
                }
555
            }
556
        } else { // we have reached the end of a path and we need to start a new row in the 'stack'
557
            if (isset($bT[$uri])) {
558
                $path = array_merge($path, array(new Breadcrumb($uri, $bT[$uri]['label'])));
559
            }
560
561
            $index = 1;
562
            $length = sizeof($path);
563
            $limit = $length - 5;
564
            foreach ($path as $crumb) {
565
                if ($length > 5 && $index > $length - $limit) { // displays 5 concepts closest to the concept.
566
                    $crumb->hideLabel();
567
                }
568
                $index++;
569
            }
570
            $crumbs[] = array_reverse($path);
571
        }
572
        return $crumbs;
573
    }
574
575
    /**
576
     * Verify that the requested language is supported by the vocabulary. If not, returns
577
     * the default language of the vocabulary.
578
     * @param string $lang language to check
579
     * @return string language tag that is supported by the vocabulary
580
     */
581
582
    public function verifyVocabularyLanguage($lang)
583
    {
584
        return (in_array($lang, $this->config->getLanguages())) ? $lang : $this->config->getDefaultLanguage();
585
    }
586
587
    /**
588
     * Returns a list of recently changed or entirely new concepts.
589
     * @param string $clang content language for the labels 
590
     * @param string $lang UI language for the dates
591
     * @return Array 
592
     */
593
    public function getChangeList($prop, $clang, $lang, $offset)
594
    {
595
      $changelist = $this->getSparql()->queryChangeList($clang, $offset, $prop);
596
      $bydate = array();
597
      foreach($changelist as $concept) {
598
        $concept['datestring'] = Punic\Calendar::formatDate($concept['date'], 'medium', $lang);
599
        $bydate[Punic\Calendar::getMonthName($concept['date'], 'wide', $lang, true) . Punic\Calendar::format($concept['date'], ' y', $lang) ][strtolower($concept['prefLabel'])] = $concept;
600
      }
601
      return $bydate;
602
    }
603
604
    public function getTitle($lang=null) {
605
      return $this->config->getTitle($lang);
606
    }
607
    
608
    public function getShortName() {
609
      return $this->config->getShortName();
610
    }
611
    
612
    public function getId() {
613
      return $this->config->getId();
614
    }
615
}
616