--- /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);
+ }
+ }
+ }
+}