server/src/app/Libraries/Mergers/CocoonAbstractRdfMerger.php
changeset 18 f2a40bbc27f6
child 19 eadaf0b8f02e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/app/Libraries/Mergers/CocoonAbstractRdfMerger.php	Tue Nov 17 13:11:55 2015 +0100
@@ -0,0 +1,200 @@
+<?php
+namespace CorpusParole\Libraries\Mergers;
+
+
+use EasyRdf\RdfNamespace;
+use EasyRdf\Graph;
+
+RdfNamespace::set('edm', 'http://www.europeana.eu/schemas/edm/');
+RdfNamespace::set('ore', 'http://www.openarchives.org/ore/terms/');
+RdfNamespace::set('crdo', 'http://crdo.risc.cnrs.fr/schemas/');
+RdfNamespace::set('olac', 'http://www.language-archives.org/OLAC/1.1/');
+RdfNamespace::set('skos', 'http://www.w3.org/2004/02/skos/core#');
+
+abstract class CocoonAbstractRdfMerger implements RdfMerger {
+
+    const ORIGIN_BASE = 0;
+    const ORIGIN_SRC = 1;
+
+    abstract protected function getTypeMergeMethodMap();
+
+    /**
+     * Merge an Rdf Graph from one model to another
+     *
+     * @param EasyRdf\Graph $baseGraph The graph used as base for the merge
+     * @param EasyRdf\Graph $srcGraph The source graph with the new triples to add
+     *
+     * @return EasyRdf\Graph The new merged graph
+     */
+    function mergeGraph($baseGraph, $srcGraph) {
+
+        $this->mergedArray = [];
+        $this->resGraph = new Graph($baseGraph->getUri());
+        $this->bnodeMerge = [];
+        $this->baseGraph = $baseGraph;
+        $this->srcGraph = $srcGraph;
+
+
+        $typeMergeMethodMap = $this->getTypeMergeMethodMap();
+
+        foreach ( $typeMergeMethodMap as $nodeType => $mergeMethod) {
+
+            foreach($baseGraph->allOfType($nodeType) as $baseResource) {
+                if($baseResource->isBNode()) {
+                    continue;
+                }
+                $this->mergedArray[$baseResource->getUri()] = $baseGraph->toRdfPhp()[$baseResource->getUri()];
+            }
+            foreach($srcGraph->allOfType($nodeType) as $srcResource) {
+                if($baseResource->isBNode()) {
+                    continue;
+                }
+                if(empty($baseGraph->propertyUris($srcResource->getUri()))) {
+                    $this->mergedArray[$srcResource->getUri()] = $srcGraph->toRdfPhp()[$srcResource->getUri()];
+                }
+                else {
+                    $baseResource = $baseGraph->resource($srcResource->getUri());
+                    $this->mergedArray[$srcResource->getUri()] = $baseGraph->toRdfPhp()[$baseResource->getUri()];
+                    call_user_func(array($this, $mergeMethod), $baseResource, $srcResource);
+                }
+            }
+        }
+
+        // merge blank node
+        reset($this->bnodeMerge);
+        while(list($bnodeId, $bnodeDef) = each($this->bnodeMerge)) {
+
+            $srcUrl = isset($bnodeDef['src_url'])?$bnodeDef['src_url']:null;
+            $baseUrl = isset($bnodeDef['base_url'])?$bnodeDef['base_url']:null;
+
+            if(is_null($srcUrl) && !is_null($baseUrl)) {
+                $this->mergedArray[$bnodeId] = $this->copyResource($baseGraph->toRdfPhp()[$baseUrl],CocoonAbstractRdfMerger::ORIGIN_BASE);
+            }
+            elseif (is_null($baseUrl) && !is_null($srcUrl)) {
+                $this->mergedArray[$bnodeId] = $this->copyResource($srcGraph->toRdfPhp()[$srcUrl],CocoonAbstractRdfMerger::ORIGIN_SRC);
+            }
+            elseif (!is_null($baseUrl) && !is_null($srcUrl)) {
+
+                $baseResource = $baseGraph->resource($baseUrl);
+                $srcResource = $srcGraph->resource($srcUrl);
+
+                $mergeMethod = $typeMergeMethodMap[$baseResource->typeAsResource()->getUri()];
+                $this->mergedArray[$bnodeId] = [];
+
+                call_user_func(array($this, $mergeMethod), $baseResource, $srcResource, $bnodeId);
+
+            }
+
+        }
+
+        //echo "MERGED ARRAY:\n";
+        $this->resGraph->parse($this->mergedArray);
+        return $this->resGraph;
+    }
+
+    /**
+     * Copy a full resource node
+     *
+     */
+    protected function copyResource($origArray, $origin) {
+        $resArray = [];
+        foreach($origArray as $prop => $propValues) {
+            $resArray[$prop] = $this->buildValueList($propValues, $origin);
+        }
+        return $resArray;
+    }
+
+    /**
+     * Build a value list. replace the blank node reference.
+     * @param $srcValues array The original values
+     * @param $origin int values are 'ORIGIN_BASE' if bnode from base graph or 'ORIGIN_SRC' from source graph
+     */
+    protected function buildValueList($srcValues, $origin) {
+        $srcArrayProps = [];
+        foreach ($srcValues as $propValue) {
+            if(is_array($propValue) && array_key_exists('type', $propValue) && $propValue['type'] == 'bnode') {
+                $newBNodeId = $this->resGraph->newBNodeId();
+                if($origin == CocoonAbstractRdfMerger::ORIGIN_SRC) {
+                    $this->bnodeMerge[$newBNodeId] = ['src_url' => $propValue['value'], 'base_url' => null];
+                }
+                else {
+                    $this->bnodeMerge[$newBNodeId] = ['base_url' => $propValue['value'], 'src_url' => null];
+                }
+
+                $propValue['value'] = $newBNodeId;
+            }
+            $srcArrayProps[] = $propValue;
+        }
+        return $srcArrayProps;
+    }
+
+    protected function mergePropertySingleValue($prop, &$targetArray, $baseArray, $srcArray) {
+
+        if(isset($baseArray[$prop])) {
+            $targetArray[$prop] = $this->buildValueList($baseArray[$prop], CocoonAbstractRdfMerger::ORIGIN_BASE);
+        }
+        elseif(isset($srcArray[$prop])) {
+            $targetArray[$prop] = $this->buildValueList($srcArray[$prop], CocoonAbstractRdfMerger::ORIGIN_SRC);
+        }
+    }
+
+    protected function mergePropertyMultiplevalue($prop, &$targetArray, $baseArray, $srcArray) {
+        $propArray = $this->buildValueList(isset($baseArray[$prop])?$baseArray[$prop]:[], CocoonAbstractRdfMerger::ORIGIN_BASE);
+        if(isset($srcArray[$prop])) {
+            $mergedArray = array_merge($propArray, $this->buildValueList($srcArray[$prop], CocoonAbstractRdfMerger::ORIGIN_BASE));
+            //yes, this is real. Array_unique does not work on multidimentional arrays. Most work-around suggest the use of serialize to compare sub arrays...
+            $propArray = array_values(array_intersect_key($mergedArray, array_unique(array_map('md5',array_map('serialize', $mergedArray)))));
+        }
+
+        if(!empty($propArray)) {
+            $targetArray[$prop] = $propArray;
+        }
+    }
+
+    protected function mergePropertySingleBNode($prop, &$targetArray, $baseArray, $srcArray) {
+
+        $newBNodeId = $this->resGraph->newBNodeId();
+
+        $srcUrl = null;
+        $baseUrl = null;
+
+        // in src but not in base
+        if(array_key_exists($prop, $baseArray) &&
+            !empty($baseArray[$prop]) &&
+            array_key_exists('type',$baseArray[$prop][0]) &&
+            array_key_exists('value',$baseArray[$prop][0]) &&
+            $baseArray[$prop][0]['type'] === 'bnode') {
+            $baseUrl = $baseArray[$prop][0]['value'];
+        }
+
+        if(array_key_exists($prop, $srcArray) &&
+            !empty($srcArray[$prop]) &&
+            array_key_exists('type',$srcArray[$prop][0]) &&
+            array_key_exists('value',$srcArray[$prop][0]) &&
+            $srcArray[$prop][0]['type'] === 'bnode') {
+            $srcUrl = $srcArray[$prop][0]['value'];
+        }
+
+        $this->bnodeMerge[$newBNodeId] = ['src_url' => $srcUrl, 'base_url' => $baseUrl];
+
+        $targetArray[$prop] = [ [ 'type' => 'bnode', 'value' => $newBNodeId], ];
+
+    }
+
+
+    protected function mergeProperties($singleBNodeProperties, $singleProperties, &$targetArray, $baseRes, $srcRes) {
+        $srcArray = $this->srcGraph->toRdfPhp()[$srcRes->getUri()];
+        $baseArray = $this->baseGraph->toRdfPhp()[$baseRes->getUri()];
+        foreach($srcRes->propertyUris() as $prop) {
+            if(in_array($prop, $singleBNodeProperties)) {
+                $this->mergePropertySingleBNode($prop, $targetArray, $baseArray, $srcArray);
+            }
+            elseif(in_array($prop, $singleProperties)) {
+                $this->mergePropertySingleValue($prop, $targetArray, $baseArray, $srcArray);
+            }
+            else {
+                $this->mergePropertyMultiplevalue($prop, $targetArray, $baseArray, $srcArray);
+            }
+        }
+    }
+}