forked from friendica/php-json-ld
Simplified relation serialization format.
This commit is contained in:
parent
98f8f76130
commit
5244670fdb
216
jsonld.php
216
jsonld.php
|
@ -118,7 +118,7 @@ function jsonld_add_context($ctx, $input)
|
||||||
{
|
{
|
||||||
foreach($rval as $v)
|
foreach($rval as $v)
|
||||||
{
|
{
|
||||||
$v->{'@context'} = _cloneContext($ctxOut);
|
$v->{'@context'} = _clone($ctxOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -160,8 +160,8 @@ function jsonld_compact($ctx, $input)
|
||||||
function jsonld_merge_contexts($ctx1, $ctx2)
|
function jsonld_merge_contexts($ctx1, $ctx2)
|
||||||
{
|
{
|
||||||
// copy contexts
|
// copy contexts
|
||||||
$merged = _cloneContext($ctx1);
|
$merged = _clone($ctx1);
|
||||||
$copy = _cloneContext($ctx2);
|
$copy = _clone($ctx2);
|
||||||
|
|
||||||
// if the new context contains any IRIs that are in the merged context,
|
// if the new context contains any IRIs that are in the merged context,
|
||||||
// remove them from the merged context, they will be overwritten
|
// remove them from the merged context, they will be overwritten
|
||||||
|
@ -541,10 +541,8 @@ function _setProperty($s, $p, $o)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones a string/number or an object and sorts the keys. Deep clone
|
* Clones an object, array, or string/number. If cloning an object, the keys
|
||||||
* is not performed. This function will deep copy arrays, but that feature
|
* will be sorted.
|
||||||
* isn't needed in this implementation at present. If it is needed in the
|
|
||||||
* future, it will have to be implemented here.
|
|
||||||
*
|
*
|
||||||
* @param value the value to clone.
|
* @param value the value to clone.
|
||||||
*
|
*
|
||||||
|
@ -561,7 +559,15 @@ function _clone($value)
|
||||||
sort($keys);
|
sort($keys);
|
||||||
foreach($keys as $key)
|
foreach($keys as $key)
|
||||||
{
|
{
|
||||||
$rval->$key = $value->$key;
|
$rval->$key = _clone($value->$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(is_array($value))
|
||||||
|
{
|
||||||
|
$rval = array();
|
||||||
|
foreach($value as $v)
|
||||||
|
{
|
||||||
|
$rval[] = _clone($v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -572,35 +578,6 @@ function _clone($value)
|
||||||
return $rval;
|
return $rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones a context.
|
|
||||||
*
|
|
||||||
* @param ctx the context to clone.
|
|
||||||
*
|
|
||||||
* @return the clone of the context.
|
|
||||||
*/
|
|
||||||
function _cloneContext($ctx)
|
|
||||||
{
|
|
||||||
$rval = new stdClass();
|
|
||||||
foreach($ctx as $key => $value)
|
|
||||||
{
|
|
||||||
// deep-copy @coerce
|
|
||||||
if($key === '@coerce')
|
|
||||||
{
|
|
||||||
$rval->{'@coerce'} = new stdClass();
|
|
||||||
foreach($ctx->{'@coerce'} as $type => $p)
|
|
||||||
{
|
|
||||||
$rval->{'@coerce'}->$type = $p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$rval->$key = $ctx->$key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the coerce type for the given property.
|
* Gets the coerce type for the given property.
|
||||||
*
|
*
|
||||||
|
@ -1449,9 +1426,16 @@ class MappingBuilder
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->count = 1;
|
$this->count = 1;
|
||||||
$this->mapped = new stdClass();
|
$this->processed = new stdClass();
|
||||||
$this->mapping = new stdClass();
|
$this->mapping = new stdClass();
|
||||||
$this->output = new stdClass();
|
$this->adj = new stdClass();
|
||||||
|
$this->keyStack = array();
|
||||||
|
$entry = new stdClass();
|
||||||
|
$entry->keys = array('s1');
|
||||||
|
$entry->idx = 0;
|
||||||
|
$this->keyStack[] = $entry;
|
||||||
|
$this->done = new stdClass();
|
||||||
|
$this->s = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1463,9 +1447,12 @@ class MappingBuilder
|
||||||
{
|
{
|
||||||
$rval = new MappingBuilder();
|
$rval = new MappingBuilder();
|
||||||
$rval->count = $this->count;
|
$rval->count = $this->count;
|
||||||
$rval->mapped = _clone($this->mapped);
|
$rval->processed = _clone($this->processed);
|
||||||
$rval->mapping = _clone($this->mapping);
|
$rval->mapping = _clone($this->mapping);
|
||||||
$rval->output = _clone($this->output);
|
$rval->adj = _clone($this->adj);
|
||||||
|
$rval->keyStack = _clone($this->keyStack);
|
||||||
|
$rval->done = _clone($this->done);
|
||||||
|
$rval->s = $this->s;
|
||||||
return $rval;
|
return $rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1941,48 +1928,49 @@ class JsonLdProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively creates a relation serialization (partial or full).
|
* Recursively increments the relation serialization for a mapping.
|
||||||
*
|
*
|
||||||
* @param keys the keys to serialize in the current output.
|
* @param mb the mapping builder to update.
|
||||||
* @param output the current mapping builder output.
|
|
||||||
* @param done the already serialized keys.
|
|
||||||
*
|
|
||||||
* @return the relation serialization.
|
|
||||||
*/
|
*/
|
||||||
public function recursiveSerializeMapping($keys, $output, $done)
|
public function serializeMapping($mb)
|
||||||
{
|
{
|
||||||
$rval = '';
|
if(count($mb->keyStack) > 0)
|
||||||
foreach($keys as $k)
|
|
||||||
{
|
{
|
||||||
if(!property_exists($output, $k))
|
// continue from top of key stack
|
||||||
|
$next = array_pop($mb->keyStack);
|
||||||
|
$len = count($next->keys);
|
||||||
|
for(; $next->idx < $len; ++$next->idx)
|
||||||
{
|
{
|
||||||
break;
|
$k = $next->keys[$next->idx];
|
||||||
}
|
if(!property_exists($mb->adj, $k))
|
||||||
|
|
||||||
if(property_exists($done, $k))
|
|
||||||
{
|
|
||||||
// mark cycle
|
|
||||||
$rval .= '_' . $k;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$done->$k = true;
|
|
||||||
$tmp = $output->$k;
|
|
||||||
foreach($tmp->k as $s)
|
|
||||||
{
|
{
|
||||||
$rval .= $s;
|
$mb->keyStack[] = $next;
|
||||||
$iri = $tmp->m->$s;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(property_exists($mb->done, $k))
|
||||||
|
{
|
||||||
|
// mark cycle
|
||||||
|
$mb->s .= '_' . $k;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// mark key as serialized
|
||||||
|
$mb->done->$k = true;
|
||||||
|
|
||||||
|
// serialize top-level key and its details
|
||||||
|
$s = $k;
|
||||||
|
$adj = $mb->adj->$k;
|
||||||
|
$iri = $adj->i;
|
||||||
if(property_exists($this->subjects, $iri))
|
if(property_exists($this->subjects, $iri))
|
||||||
{
|
{
|
||||||
$b = $this->subjects->$iri;
|
$b = $this->subjects->$iri;
|
||||||
|
|
||||||
// serialize properties
|
// serialize properties
|
||||||
$rval .= '<';
|
$s .= '<' . $this->serializeProperties($b) . '>';
|
||||||
$rval .= $this->serializeProperties($b);
|
|
||||||
$rval .= '>';
|
|
||||||
|
|
||||||
// serialize references
|
// serialize references
|
||||||
$rval .= '<';
|
$s .= '<';
|
||||||
$first = true;
|
$first = true;
|
||||||
$refs = $this->edges->refs->$iri->all;
|
$refs = $this->edges->refs->$iri->all;
|
||||||
foreach($refs as $r)
|
foreach($refs as $r)
|
||||||
|
@ -1993,52 +1981,46 @@ class JsonLdProcessor
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$rval .= '|';
|
$s .= '|';
|
||||||
}
|
}
|
||||||
$rval .= _isBlankNodeIri($r->s) ? '_:' : $refs->s;
|
$s .= _isBlankNodeIri($r->s) ? '_:' : $refs->s;
|
||||||
}
|
}
|
||||||
$rval .= '>';
|
$s .= '>';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$rval .= $this->recursiveSerializeMapping($tmp->k, $output, $done);
|
// serialize adjacent node keys
|
||||||
|
$s .= implode($adj->k);
|
||||||
|
$mb->s .= $s;
|
||||||
|
$entry = new stdClass();
|
||||||
|
$entry->keys = $adj->k;
|
||||||
|
$entry->idx = 0;
|
||||||
|
$mb->keyStack[] = $entry;
|
||||||
|
$this->serializeMapping($mb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a relation serialization (partial or full).
|
|
||||||
*
|
|
||||||
* @param output the current mapping builder output.
|
|
||||||
*
|
|
||||||
* @return the relation serialization.
|
|
||||||
*/
|
|
||||||
public function serializeMapping($output)
|
|
||||||
{
|
|
||||||
return $this->recursiveSerializeMapping(
|
|
||||||
array('s1'), $output, new stdClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively serializes adjacent bnode combinations.
|
* Recursively serializes adjacent bnode combinations.
|
||||||
*
|
*
|
||||||
* @param s the serialization to update.
|
* @param s the serialization to update.
|
||||||
* @param top the top of the serialization.
|
* @param iri the IRI of the bnode being serialized.
|
||||||
|
* @param siri the serialization name for the bnode IRI.
|
||||||
* @param mb the MappingBuilder to use.
|
* @param mb the MappingBuilder to use.
|
||||||
* @param dir the edge direction to use ('props' or 'refs').
|
* @param dir the edge direction to use ('props' or 'refs').
|
||||||
* @param mapped all of the already-mapped adjacent bnodes.
|
* @param mapped all of the already-mapped adjacent bnodes.
|
||||||
* @param notMapped all of the not-yet mapped adjacent bnodes.
|
* @param notMapped all of the not-yet mapped adjacent bnodes.
|
||||||
*/
|
*/
|
||||||
public function serializeCombos(
|
public function serializeCombos(
|
||||||
$s, $top, $mb, $dir, $mapped, $notMapped)
|
$s, $iri, $siri, $mb, $dir, $mapped, $notMapped)
|
||||||
{
|
{
|
||||||
// copy mapped nodes
|
|
||||||
$mapped = _clone($mapped);
|
|
||||||
|
|
||||||
// handle recursion
|
// handle recursion
|
||||||
if(count($notMapped) > 0)
|
if(count($notMapped) > 0)
|
||||||
{
|
{
|
||||||
|
// copy mapped nodes
|
||||||
|
$mapped = _clone($mapped);
|
||||||
|
|
||||||
// map first bnode in list
|
// map first bnode in list
|
||||||
$mapped->{$mb->mapNode($notMapped[0]->s)} = $notMapped[0]->s;
|
$mapped->{$mb->mapNode($notMapped[0]->s)} = $notMapped[0]->s;
|
||||||
|
|
||||||
|
@ -2049,46 +2031,43 @@ class JsonLdProcessor
|
||||||
for($r = 0; $r < $rotations; ++$r)
|
for($r = 0; $r < $rotations; ++$r)
|
||||||
{
|
{
|
||||||
$m = ($r === 0) ? $mb : $original->copy();
|
$m = ($r === 0) ? $mb : $original->copy();
|
||||||
$this->serializeCombos($s, $top, $m, $dir, $mapped, $notMapped);
|
$this->serializeCombos(
|
||||||
|
$s, $iri, $siri, $m, $dir, $mapped, $notMapped);
|
||||||
|
|
||||||
// rotate not-mapped for next combination
|
// rotate not-mapped for next combination
|
||||||
_rotate($notMapped);
|
_rotate($notMapped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle final adjacent node in current combination
|
// no more adjacent bnodes to map, update serialization
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$keys = array_keys((array)$mapped);
|
$keys = array_keys((array)$mapped);
|
||||||
sort($keys);
|
sort($keys);
|
||||||
$mb->output->$top = new stdClass();
|
$entry = new stdClass();
|
||||||
$mb->output->$top->k = $keys;
|
$entry->i = $iri;
|
||||||
$mb->output->$top->m = $mapped;
|
$entry->k = $keys;
|
||||||
|
$entry->m = $mapped;
|
||||||
|
$mb->adj->$siri = $entry;
|
||||||
|
$this->serializeMapping($mb);
|
||||||
|
|
||||||
// optimize away mappings that are already too large
|
// optimize away mappings that are already too large
|
||||||
$_s = $this->serializeMapping($mb->output);
|
if($s->$dir === null or
|
||||||
if($s->$dir === null or _compareSerializations($_s, $s->$dir->s) <= 0)
|
_compareSerializations($mb->s, $s->$dir->s) <= 0)
|
||||||
{
|
{
|
||||||
$oldCount = $mb->count;
|
|
||||||
|
|
||||||
// recurse into adjacent values
|
// recurse into adjacent values
|
||||||
foreach($keys as $i => $k)
|
foreach($keys as $i => $k)
|
||||||
{
|
{
|
||||||
$this->serializeBlankNode($s, $mapped->$k, $mb, $dir);
|
$this->serializeBlankNode($s, $mapped->$k, $mb, $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reserialize if more nodes were mapped
|
|
||||||
if($mb->count > $oldCount)
|
|
||||||
{
|
|
||||||
$_s = $this->serializeMapping($mb->output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update least serialization if new one has been found
|
// update least serialization if new one has been found
|
||||||
|
$this->serializeMapping($mb);
|
||||||
if($s->$dir === null or
|
if($s->$dir === null or
|
||||||
(_compareSerializations($_s, $s->$dir->s) <= 0 and
|
(_compareSerializations($mb->s, $s->$dir->s) <= 0 and
|
||||||
count($_s) >= count($s->$dir->s)))
|
strlen($mb->s) >= strlen($s->$dir->s)))
|
||||||
{
|
{
|
||||||
$s->$dir = new stdClass();
|
$s->$dir = new stdClass();
|
||||||
$s->$dir->s = $_s;
|
$s->$dir->s = $mb->s;
|
||||||
$s->$dir->m = $mb->mapping;
|
$s->$dir->m = $mb->mapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2105,12 +2084,12 @@ class JsonLdProcessor
|
||||||
*/
|
*/
|
||||||
public function serializeBlankNode($s, $iri, $mb, $dir)
|
public function serializeBlankNode($s, $iri, $mb, $dir)
|
||||||
{
|
{
|
||||||
// only do mapping if iri not already mapped
|
// only do mapping if iri not already processed
|
||||||
if(!property_exists($mb->mapped, $iri))
|
if(!property_exists($mb->processed, $iri))
|
||||||
{
|
{
|
||||||
// iri now mapped
|
// iri now processed
|
||||||
$mb->mapped->$iri = true;
|
$mb->processed->$iri = true;
|
||||||
$top = $mb->mapNode($iri);
|
$siri = $mb->mapNode($iri);
|
||||||
|
|
||||||
// copy original mapping builder
|
// copy original mapping builder
|
||||||
$original = $mb->copy();
|
$original = $mb->copy();
|
||||||
|
@ -2155,7 +2134,8 @@ class JsonLdProcessor
|
||||||
for($i = 0; $i < $combos; ++$i)
|
for($i = 0; $i < $combos; ++$i)
|
||||||
{
|
{
|
||||||
$m = ($i === 0) ? $mb : $original->copy();
|
$m = ($i === 0) ? $mb : $original->copy();
|
||||||
$this->serializeCombos($s, $top, $mb, $dir, $mapped, $notMapped);
|
$this->serializeCombos(
|
||||||
|
$s, $iri, $siri, $mb, $dir, $mapped, $notMapped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue