<?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, $uri = null) {
$uri = is_null($uri)?$baseGraph->getUri():$uri;
$this->mergedArray = [];
$this->resGraph = new Graph($uri);
$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($srcResource->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)) {
$copyResource = $this->copyResource($baseGraph->toRdfPhp()[$baseUrl],CocoonAbstractRdfMerger::ORIGIN_BASE);
$this->mergedArray[$bnodeId] = $copyResource;
}
elseif (is_null($baseUrl) && !is_null($srcUrl)) {
$copyResource = $this->copyResource($srcGraph->toRdfPhp()[$srcUrl],CocoonAbstractRdfMerger::ORIGIN_SRC);
$this->mergedArray[$bnodeId] = $copyResource;
}
elseif (!is_null($baseUrl) && !is_null($srcUrl)) {
$baseResource = $baseGraph->resource($baseUrl);
$srcResource = $srcGraph->resource($srcUrl);
if(!is_null($baseResource->typeAsResource()) && array_key_exists($baseResource->typeAsResource()->getUri(), $typeMergeMethodMap)) {
$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) {
$srcRdfPhp = $this->srcGraph->toRdfPhp();
$srcArray = array_key_exists($srcRes->getUri(), $srcRdfPhp)?$srcRdfPhp[$srcRes->getUri()]:[];
$baseRdfPhp = $this->baseGraph->toRdfPhp();
$baseArray = array_key_exists($baseRes->getUri(), $baseRdfPhp)?$baseRdfPhp[$baseRes->getUri()]:[];
foreach(array_unique(array_merge($srcRes->propertyUris(), $baseRes->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);
}
}
}
}