--- a/playground/jsonld.js Mon Aug 01 23:21:13 2011 -0400
+++ b/playground/jsonld.js Tue Aug 02 01:51:55 2011 -0400
@@ -254,10 +254,8 @@
};
/**
- * Clones a string/number or an object and sorts the keys. Deep clone
- * is not performed. This function will deep copy arrays, but that feature
- * isn't needed in this implementation at present. If it is needed in the
- * future, it will have to be implemented here.
+ * Clones an object, array, or string/number. If cloning an object, the keys
+ * will be sorted.
*
* @param value the value to clone.
*
@@ -274,14 +272,15 @@
for(var i in keys)
{
var key = keys[i];
- if(value[key].constructor === Array)
- {
- rval[key] = value[key].slice();
- }
- else
- {
- rval[key] = value[key];
- }
+ rval[key] = _clone(value[key]);
+ }
+ }
+ else if(value.constructor === Array)
+ {
+ rval = [];
+ for(var i in value)
+ {
+ rval[i] = _clone(value[i]);
}
}
else
@@ -293,36 +292,6 @@
};
/**
- * Clones a context.
- *
- * @param ctx the context to clone.
- *
- * @return the clone of the context.
- */
-var _cloneContext = function(ctx)
-{
- var rval = {};
- for(var key in ctx)
- {
- // deep-copy @coerce
- if(key === '@coerce')
- {
- rval['@coerce'] = {};
- for(var type in ctx['@coerce'])
- {
- var p = ctx['@coerce'][type];
- rval['@coerce'][type] = (p.constructor === Array) ? p.slice() : p;
- }
- }
- else
- {
- rval[key] = ctx[key];
- }
- }
- return rval;
-};
-
-/**
* Gets the coerce type for the given property.
*
* @param ctx the context to use.
@@ -1463,9 +1432,12 @@
MappingBuilder = function()
{
this.count = 1;
- this.mapped = {};
+ this.processed = {};
this.mapping = {};
- this.output = {};
+ this.adj = {};
+ this.keyStack = [{ keys: ['s1'], idx: 0 }];
+ this.done = {};
+ this.s = '';
};
/**
@@ -1477,9 +1449,12 @@
{
var rval = new MappingBuilder();
rval.count = this.count;
- rval.mapped = _clone(this.mapped);
+ rval.processed = _clone(this.processed);
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;
};
@@ -1509,32 +1484,6 @@
};
/**
- * Marks a relation serialization as dirty if necessary.
- *
- * @param iri the IRI of the bnode to check.
- * @param changed the old IRI of the bnode that changed.
- * @param dir the direction to check ('props' or 'refs').
- */
-jsonld.Processor.prototype.markSerializationDirty = function(iri, changed, dir)
-{
- var s = this.serializations[iri];
- if(s[dir] !== null && changed in s[dir].m)
- {
- s[dir] = null;
- }
-};
-
-/**
- * Rotates the elements in an array one position.
- *
- * @param a the array.
- */
-var _rotate = function(a)
-{
- a.unshift.apply(a, a.splice(1, a.length));
-};
-
-/**
* Serializes the properties of the given bnode for its relation serialization.
*
* @param b the blank node.
@@ -1578,53 +1527,53 @@
};
/**
- * 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 output the current mapping builder output.
- * @param done the already serialized keys.
- *
- * @return the relation serialization.
+ * @param subjects the subjects in the graph.
+ * @param edges the edges in the graph.
*/
-jsonld.Processor.prototype.recursiveSerializeMapping = function(
- keys, output, done)
+MappingBuilder.prototype.serialize = function(subjects, edges)
{
- var rval = '';
- for(var i in keys)
+ if(this.keyStack.length > 0)
{
- var k = keys[i];
- if(!(k in output))
+ // continue from top of key stack
+ var next = this.keyStack.pop();
+ for(; next.idx < next.keys.length; ++next.idx)
{
- break;
- }
-
- if(k in done)
- {
- // mark cycle
- rval += '_' + k;
- }
- else
- {
- done[k] = true;
- var tmp = output[k];
- for(var t in tmp.k)
+ var k = next.keys[next.idx];
+ if(!(k in this.adj))
{
- var s = tmp.k[t];
- rval += s;
- var iri = tmp.m[s];
- if(iri in this.subjects)
+ this.keyStack.push(next);
+ break;
+ }
+
+ if(k in this.done)
+ {
+ // mark cycle
+ this.s += '_' + k;
+ }
+ else
+ {
+ // mark key as serialized
+ this.done[k] = true;
+
+ // serialize top-level key and its details
+ var s = k;
+ var adj = this.adj[k];
+ var iri = adj.i;
+ if(iri in subjects)
{
- var b = this.subjects[iri];
+ var b = subjects[iri];
// serialize properties
- rval += '<';
- rval += _serializeProperties(b);
- rval += '>';
+ s += '<';
+ s += _serializeProperties(b);
+ s += '>';
// serialize references
- rval += '<';
+ s += '<';
var first = true;
- var refs = this.edges.refs[iri].all;
+ var refs = edges.refs[iri].all;
for(var r in refs)
{
if(first)
@@ -1633,29 +1582,47 @@
}
else
{
- rval += '|';
+ s += '|';
}
- rval += _isBlankNodeIri(refs[r].s) ? '_:' : refs[r].s;
+ s += _isBlankNodeIri(refs[r].s) ? '_:' : refs[r].s;
}
- rval += '>';
+ s += '>';
}
+
+ // serialize adjacent node keys
+ s += adj.k.join('');
+ this.s += s;
+ this.keyStack.push({ keys: adj.k, idx: 0 });
+ this.serialize(subjects, edges);
}
- rval += this.recursiveSerializeMapping(tmp.k, output, done);
}
}
- return rval;
};
/**
- * Creates a relation serialization (partial or full).
- *
- * @param output the current mapping builder output.
+ * Marks a relation serialization as dirty if necessary.
*
- * @return the relation serialization.
+ * @param iri the IRI of the bnode to check.
+ * @param changed the old IRI of the bnode that changed.
+ * @param dir the direction to check ('props' or 'refs').
*/
-jsonld.Processor.prototype.serializeMapping = function(output)
+jsonld.Processor.prototype.markSerializationDirty = function(iri, changed, dir)
{
- return this.recursiveSerializeMapping(['s1'], output, {});
+ var s = this.serializations[iri];
+ if(s[dir] !== null && changed in s[dir].m)
+ {
+ s[dir] = null;
+ }
+};
+
+/**
+ * Rotates the elements in an array one position.
+ *
+ * @param a the array.
+ */
+var _rotate = function(a)
+{
+ a.unshift.apply(a, a.splice(1, a.length));
};
/**
@@ -1689,24 +1656,25 @@
};
/**
- * Recursively serializes adjacent bnode combinations.
+ * Recursively serializes adjacent bnode combinations for a bnode.
*
* @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 dir the edge direction to use ('props' or 'refs').
* @param mapped all of the already-mapped adjacent bnodes.
* @param notMapped all of the not-yet mapped adjacent bnodes.
*/
jsonld.Processor.prototype.serializeCombos = function(
- s, top, mb, dir, mapped, notMapped)
+ s, iri, siri, mb, dir, mapped, notMapped)
{
- // copy mapped nodes
- mapped = _clone(mapped);
-
// handle recursion
if(notMapped.length > 0)
{
+ // copy mapped nodes
+ mapped = _clone(mapped);
+
// map first bnode in list
mapped[mb.mapNode(notMapped[0].s)] = notMapped[0].s;
@@ -1717,24 +1685,22 @@
for(var r = 0; r < rotations; ++r)
{
var 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(notMapped);
}
}
- // handle final adjacent node in current combination
+ // no more adjacent bnodes to map, update serialization
else
{
var keys = Object.keys(mapped).sort();
- mb.output[top] = { k: keys, m: mapped };
+ mb.adj[siri] = { i: iri, k: keys, m: mapped };
+ mb.serialize(this.subjects, this.edges);
// optimize away mappings that are already too large
- var _s = this.serializeMapping(mb.output);
- if(s[dir] === null || _compareSerializations(_s, s[dir].s) <= 0)
+ if(s[dir] === null || _compareSerializations(mb.s, s[dir].s) <= 0)
{
- var oldCount = mb.count;
-
// recurse into adjacent values
for(var i in keys)
{
@@ -1742,18 +1708,13 @@
this.serializeBlankNode(s, mapped[k], mb, dir);
}
- // reserialize if more nodes were mapped
- if(mb.count > oldCount)
+ // update least serialization if new one has been found
+ mb.serialize(this.subjects, this.edges);
+ if(s[dir] === null ||
+ (_compareSerializations(mb.s, s[dir].s) <= 0 &&
+ mb.s.length >= s[dir].s.length))
{
- _s = this.serializeMapping(mb.output);
- }
-
- // update least serialization if new one has been found
- if(s[dir] === null ||
- (_compareSerializations(_s, s[dir].s) <= 0 &&
- _s.length >= s[dir].s.length))
- {
- s[dir] = { s: _s, m: mb.mapping };
+ s[dir] = { s: mb.s, m: mb.mapping };
}
}
}
@@ -1769,12 +1730,12 @@
*/
jsonld.Processor.prototype.serializeBlankNode = function(s, iri, mb, dir)
{
- // only do mapping if iri not already mapped
- if(!(iri in mb.mapped))
+ // only do mapping if iri not already processed
+ if(!(iri in mb.processed))
{
- // iri now mapped
- mb.mapped[iri] = true;
- var top = mb.mapNode(iri);
+ // iri now processed
+ mb.processed[iri] = true;
+ var siri = mb.mapNode(iri);
// copy original mapping builder
var original = mb.copy();
@@ -1819,7 +1780,7 @@
for(var i = 0; i < combos; ++i)
{
var m = (i === 0) ? mb : original.copy();
- this.serializeCombos(s, top, mb, dir, mapped, notMapped);
+ this.serializeCombos(s, iri, siri, mb, dir, mapped, notMapped);
}
}
};
@@ -2461,7 +2422,7 @@
{
for(var i in rval)
{
- rval[i]['@context'] = _cloneContext(ctxOut);
+ rval[i]['@context'] = _clone(ctxOut);
}
}
else
@@ -2499,8 +2460,8 @@
jsonld.mergeContexts = function(ctx1, ctx2)
{
// copy contexts
- var merged = _cloneContext(ctx1);
- var copy = _cloneContext(ctx2);
+ var merged = _clone(ctx1);
+ var copy = _clone(ctx2);
// if the new context contains any IRIs that are in the merged context,
// remove them from the merged context, they will be overwritten