Update flatten algorithm, fix @list merge bugs.

This commit is contained in:
Dave Longley 2012-05-22 15:09:26 -04:00
parent 160c1b379a
commit f17859714b

View file

@ -691,26 +691,36 @@ class JsonLdProcessor {
* @param stdClass $subject the subject to add the value to. * @param stdClass $subject the subject to add the value to.
* @param string $property the property that relates the value to the subject. * @param string $property the property that relates the value to the subject.
* @param mixed $value the value to add. * @param mixed $value the value to add.
* @param bool [$propertyIsArray] true if the property is always an array, * @param bool [$property_is_array] true if the property is always an array,
* false if not (default: false). * false if not (default: false).
* @param bool [$property_is_list] true if the property is a @list, false
* if not (default: false).
*/ */
public static function addValue( public static function addValue(
$subject, $property, $value, $propertyIsArray=false) { $subject, $property, $value,
$property_is_array=false, $property_is_list=false) {
if($property === '@list') {
$property_is_list = true;
}
if(is_array($value)) { if(is_array($value)) {
if(count($value) === 0 && $propertyIsArray && if(count($value) === 0 && $property_is_array &&
!property_exists($subject, $property)) { !property_exists($subject, $property)) {
$subject->{$property} = array(); $subject->{$property} = array();
} }
foreach($value as $v) { foreach($value as $v) {
self::addValue($subject, $property, $v, $propertyIsArray); self::addValue(
$subject, $property, $v, $property_is_array, $property_is_list);
} }
} }
else if(property_exists($subject, $property)) { else if(property_exists($subject, $property)) {
$has_value = self::hasValue($subject, $property, $value); // check if subject already has value unless property is list
$has_value = (!$property_is_list &&
self::hasValue($subject, $property, $value));
// make property an array if value not present or always an array // make property an array if value not present or always an array
if(!is_array($subject->{$property}) && if(!is_array($subject->{$property}) &&
(!$has_value || $propertyIsArray)) { (!$has_value || $property_is_array)) {
$subject->{$property} = array($subject->{$property}); $subject->{$property} = array($subject->{$property});
} }
@ -721,7 +731,7 @@ class JsonLdProcessor {
} }
else { else {
// add new value as set or single value // add new value as set or single value
$subject->{$property} = $propertyIsArray ? array($value) : $value; $subject->{$property} = $property_is_array ? array($value) : $value;
} }
} }
@ -754,11 +764,11 @@ class JsonLdProcessor {
* @param stdClass $subject the subject. * @param stdClass $subject the subject.
* @param string $property the property that relates the value to the subject. * @param string $property the property that relates the value to the subject.
* @param mixed $value the value to remove. * @param mixed $value the value to remove.
* @param bool [$propertyIsArray] true if the property is always an array, * @param bool [$property_is_array] true if the property is always an array,
* false if not (default: false). * false if not (default: false).
*/ */
public static function removeValue( public static function removeValue(
$subject, $property, $value, $propertyIsArray=false) { $subject, $property, $value, $property_is_array=false) {
// filter out value // filter out value
$filter = function($e) use ($value) { $filter = function($e) use ($value) {
@ -770,7 +780,7 @@ class JsonLdProcessor {
if(count($values) === 0) { if(count($values) === 0) {
self::removeProperty($subject, $property); self::removeProperty($subject, $property);
} }
else if(count($values) === 1 && !$propertyIsArray) { else if(count($values) === 1 && !$property_is_array) {
$subject->{$property} = $values[0]; $subject->{$property} = $values[0];
} }
else { else {
@ -1242,11 +1252,12 @@ class JsonLdProcessor {
// if @container is @set or @list or value is an empty array, use // if @container is @set or @list or value is an empty array, use
// an array when adding value // an array when adding value
$isArray = ($container === '@set' || $container === '@list' || $is_array = ($container === '@set' || $container === '@list' ||
(is_array($v) && count($v) === 0)); (is_array($v) && count($v) === 0));
// add compact value // add compact value
self::addValue($rval, $prop, $v, $isArray); self::addValue(
$rval, $prop, $v, $is_array, ($container === '@list'));
} }
} }
return $rval; return $rval;
@ -1472,11 +1483,17 @@ class JsonLdProcessor {
// create framing state // create framing state
$state = (object)array( $state = (object)array(
'options' => $options, 'options' => $options,
'subjects' => new stdClass()); 'graphs' => (object)array(
'@default' => new stdClass(),
'@merged' => new stdClass()));
// produce a map of all subjects and name each bnode // produce a map of all graphs and name each bnode
$namer = new UniqueNamer('_:t'); $namer = new UniqueNamer('_:t');
$this->_flatten($state->subjects, $input, $namer, null, null); $this->_flatten($input, $state->graphs, '@default', $namer, null, null);
$namer = new UniqueNamer('_:t');
$this->_flatten($input, $state->graphs, '@merged', $namer, null, null);
// FIXME: currently uses subjects from @merged graph only
$state->subjects = $state->graphs->{'@merged'};
// frame the subjects // frame the subjects
$framed = new ArrayObject(); $framed = new ArrayObject();
@ -2303,98 +2320,111 @@ class JsonLdProcessor {
/** /**
* Recursively flattens the subjects in the given JSON-LD expanded input. * Recursively flattens the subjects in the given JSON-LD expanded input.
* *
* @param stdClass $subjects a map of subject @id to subject.
* @param mixed $input the JSON-LD expanded input. * @param mixed $input the JSON-LD expanded input.
* @param stdClass $graphs a map of graph name to subject map.
* @[ara, string $graph the name of the current graph.
* @param UniqueNamer $namer the blank node namer. * @param UniqueNamer $namer the blank node namer.
* @param mixed $name the name assigned to the current input if it is a bnode. * @param mixed $name the name assigned to the current input if it is a bnode.
* @param mixed $list the list to append to, null for none. * @param mixed $list the list to append to, null for none.
*/ */
protected function _flatten($subjects, $input, $namer, $name, $list) { protected function _flatten($input, $graphs, $graph, $namer, $name, $list) {
// recurse through array // recurse through array
if(is_array($input)) { if(is_array($input)) {
foreach($input as $e) { foreach($input as $e) {
$this->_flatten($subjects, $e, $namer, null, $list); $this->_flatten($e, $graphs, $graph, $namer, null, $list);
} }
return;
} }
// handle subject
else if(is_object($input)) {
// add value to list
if(self::_isValue($input) && $list) {
$list[] = $input;
return;
}
// get name for subject // add non-object or value
if($name === null) { if(!is_object($input) || self::_isValue($input)) {
if(property_exists($input, '@id')) {
$name = $input->{'@id'};
}
if(self::_isBlankNode($input)) {
$name = $namer->getName($name);
}
}
// add subject reference to list
if($list !== null) { if($list !== null) {
$list[] = (object)array('@id' => $name); $list[] = $input;
} }
return;
}
// create new subject or merge into existing one // Note: At this point, input must be a subject.
if(property_exists($subjects, $name)) {
$subject = $subjects->{$name}; // get name for subject
if($name === null) {
if(property_exists($input, '@id')) {
$name = $input->{'@id'};
} }
else { if(self::_isBlankNode($input)) {
$subject = $subjects->{$name} = new stdClass(); $name = $namer->getName($name);
}
$subject->{'@id'} = $name;
foreach($input as $prop => $objects) {
// skip @id
if($prop === '@id') {
continue;
}
// copy non-@type keywords
if($prop !== '@type' && self::_isKeyword($prop)) {
$subject->{$prop} = $objects;
continue;
}
// iterate over objects
foreach($objects as $o) {
// handle embedded subject or subject reference
if(self::_isSubject($o) || self::_isSubjectReference($o)) {
// rename blank node @id
$id = property_exists($o, '@id') ? $o->{'@id'} : null;
if(self::_isBlankNode($o)) {
$id = $namer->getName($id);
}
// add reference and recurse
self::addValue($subject, $prop, (object)array('@id' => $id), true);
$this->_flatten($subjects, $o, $namer, $id, null);
}
else {
// recurse into list
if(self::_isList($o)) {
$l = new ArrayObject();
$this->_flatten($subjects, $o->{'@list'}, $namer, $name, $l);
$o = (object)array('@list' => (array)$l);
}
// special-handle @type blank nodes
else if($prop === '@type' && strpos($o, '_:') === 0) {
$o = $namer->getName($o);
}
// add non-subject
self::addValue($subject, $prop, $o, true);
}
}
} }
} }
// add non-object to list
else if($list !== null) { // add subject reference to list
$list[] = $input; if($list !== null) {
$list[] = (object)array('@id' => $name);
}
// create new subject or merge into existing one
if(!property_exists($graphs, $graph)) {
$graphs->{$graph} = new stdClass();
}
$subjects = $graphs->{$graph};
if(!property_exists($subjects, $name)) {
$subjects->{$name} = new stdClass();
}
$subject = $subjects->{$name};
$subject->{'@id'} = $name;
foreach($input as $prop => $objects) {
// skip @id
if($prop === '@id') {
continue;
}
// recurse into graph
if($prop === '@graph') {
// add graph subjects map entry
if(!property_exists($graphs, $name)) {
$graphs->{$name} = new stdClass();
}
$g = ($graph === '@merged') ? $graph : $name;
$this->_flatten($objects, $graphs, $g, $namer, null, null);
continue;
}
// copy non-@type keywords
if($prop !== '@type' && self::_isKeyword($prop)) {
$subject->{$prop} = $objects;
continue;
}
// iterate over objects
foreach($objects as $o) {
// handle embedded subject or subject reference
if(self::_isSubject($o) || self::_isSubjectReference($o)) {
// rename blank node @id
$id = property_exists($o, '@id') ? $o->{'@id'} : null;
if(self::_isBlankNode($o)) {
$id = $namer->getName($id);
}
// add reference and recurse
self::addValue($subject, $prop, (object)array('@id' => $id), true);
$this->_flatten($o, $graphs, $graph, $namer, $id, null);
}
else {
// recurse into list
if(self::_isList($o)) {
$_list = new ArrayObject();
$this->_flatten(
$o->{'@list'}, $graphs, $graph, $namer, $name, $_list);
$o = (object)array('@list' => (array)$_list);
}
// special-handle @type blank nodes
else if($prop === '@type' && strpos($o, '_:') === 0) {
$o = $namer->getName($o);
}
// add non-subject
self::addValue($subject, $prop, $o, true);
}
}
} }
} }