--- a/playground/jsonld.js Mon Jul 18 17:17:44 2011 -0700
+++ b/playground/jsonld.js Sun Jul 24 23:23:00 2011 -0400
@@ -100,7 +100,7 @@
for(var key in ctx)
{
// skip special context keys (start with '@')
- if(key.length > 0 && key.indexOf('@') !== 0)
+ if(key.length > 0 && key[0] !== '@')
{
// compact to a term
if(iri === ctx[key])
@@ -127,7 +127,7 @@
for(var key in ctx)
{
// skip special context keys (start with '@')
- if(key.length > 0 && key.indexOf('@') !== 0)
+ if(key.length > 0 && key[0] !== '@')
{
// see if IRI begins with the next IRI from the context
var ctxIri = ctx[key];
@@ -136,7 +136,7 @@
// compact to a CURIE
if(idx === 0 && iri.length > ctxIri.length)
{
- rval = key + ':' + iri.substr(idx + ctxIri.length);
+ rval = key + ':' + iri.substr(ctxIri.length);
if(usedCtx !== null)
{
usedCtx[key] = ctxIri;
@@ -620,7 +620,7 @@
{
_setProperty(rval, key, _clone(value[key]));
}
- else if(key !== '@context' && key !== '@coerce')
+ else if(key !== '@context')
{
// set object to expanded property
_setProperty(
@@ -1342,17 +1342,18 @@
{
var bnode = bnodes[i];
var iri = bnode[__s]['@iri'];
+ if(c14n.inNamespace(iri))
+ {
+ // generate names until one is unique
+ while(ngTmp.next() in subjects);
+ this.renameBlankNode(bnode, ngTmp.current());
+ iri = bnode[__s]['@iri'];
+ }
this.serializations[iri] =
{
'props': null,
'refs': null
};
- if(c14n.inNamespace(iri))
- {
- // generate names until one is unique
- while(ngTmp.next() in subjects);
- this.renameBlankNode(bnode, ngTmp.current());
- }
}
// keep sorting and naming blank nodes until they are all named
@@ -1365,7 +1366,7 @@
});
// name all bnodes according to the first bnode's relation mappings
- var bnode = bnodes.shift(1);
+ var bnode = bnodes.shift();
var iri = bnode[__s]['@iri'];
var dirs = ['props', 'refs'];
for(var d in dirs)
@@ -1394,11 +1395,11 @@
var renamed = [];
for(var i in keys)
{
- var iri = keys[i];
- if(!c14n.inNamespace(iri) && iri in subjects)
+ var iriK = keys[i];
+ if(!c14n.inNamespace(iri) && iriK in subjects)
{
- this.renameBlankNode(subjects[iri], c14n.next());
- renamed.push(iri);
+ this.renameBlankNode(subjects[iriK], c14n.next());
+ renamed.push(iriK);
}
}
@@ -1453,9 +1454,9 @@
};
/**
- * Copies a MappingBuilder.
+ * Copies this MappingBuilder.
*
- * @param mb the MappingBuilder to copy.
+ * @return the MappingBuilder copy.
*/
MappingBuilder.prototype.copy = function()
{
@@ -1519,6 +1520,49 @@
};
/**
+ * Serializes the properties of the given bnode for its relation serialization.
+ *
+ * @param b the blank node.
+ *
+ * @return the serialized properties.
+ */
+var _serializeProperties = function(b)
+{
+ var rval = '';
+
+ for(var p in b)
+ {
+ if(p !== '@subject')
+ {
+ var first = true;
+ var objs = (b[p].constructor === Array) ? b[p] : [b[p]];
+ for(var oi in objs)
+ {
+ if(first)
+ {
+ first = false;
+ }
+ else
+ {
+ rval += '|';
+ }
+ if(objs[oi].constructor === Object &&
+ '@iri' in objs[oi] && _isBlankNodeIri(objs[oi]['@iri']))
+ {
+ rval += '_:';
+ }
+ else
+ {
+ rval += JSON.stringify(objs[oi]);
+ }
+ }
+ }
+ }
+
+ return rval;
+};
+
+/**
* Recursively creates a relation serialization (partial or full).
*
* @param keys the keys to serialize in the current output.
@@ -1527,7 +1571,8 @@
*
* @return the relation serialization.
*/
-var _recursiveSerializeMapping = function(keys, output, done)
+jsonld.Processor.prototype.recursiveSerializeMapping = function(
+ keys, output, done)
{
var rval = '';
for(var i in keys)
@@ -1547,8 +1592,40 @@
{
done[k] = true;
var tmp = output[k];
- rval += k + tmp.join('');
- rval += _recursiveSerializeMapping(tmp, output, done);
+ for(var t in tmp.k)
+ {
+ var s = tmp.k[t];
+ rval += s;
+ var iri = tmp.m[s];
+ if(iri in this.subjects)
+ {
+ var b = this.subjects[iri];
+
+ // serialize properties
+ rval += '<';
+ rval += _serializeProperties(b);
+ rval += '>';
+
+ // serialize references
+ rval += '<';
+ var first = true;
+ var refs = this.edges.refs[iri].all;
+ for(var r in refs)
+ {
+ if(first)
+ {
+ first = false;
+ }
+ else
+ {
+ rval += '|';
+ }
+ rval += _isBlankNodeIri(refs[r].s) ? '_:' : refs[r].s;
+ }
+ rval += '>';
+ }
+ }
+ rval += this.recursiveSerializeMapping(tmp.k, output, done);
}
}
return rval;
@@ -1561,12 +1638,9 @@
*
* @return the relation serialization.
*/
-var _serializeMapping = function(output)
+jsonld.Processor.prototype.serializeMapping = function(output)
{
- // get sorted keys for current output
- var keys = Object.keys(output).sort();
- var done = {};
- return _recursiveSerializeMapping(keys, output, done);
+ return this.recursiveSerializeMapping(['s1'], output, {});
};
/**
@@ -1600,6 +1674,77 @@
};
/**
+ * Recursively serializes adjacent bnode combinations.
+ *
+ * @param s the serialization to update.
+ * @param top the top of the serialization.
+ * @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)
+{
+ // copy mapped nodes
+ mapped = _clone(mapped);
+
+ // handle recursion
+ if(notMapped.length > 0)
+ {
+ // map first bnode in list
+ mapped[mb.mapNode(notMapped[0].s)] = notMapped[0].s;
+
+ // recurse into remaining possible combinations
+ var original = mb.copy();
+ notMapped = notMapped.slice(1);
+ var rotations = Math.max(1, notMapped.length);
+ for(var r = 0; r < rotations; ++r)
+ {
+ var m = (r === 0) ? mb : original.copy();
+ this.serializeCombos(s, top, m, dir, mapped, notMapped);
+
+ // rotate not-mapped for next combination
+ _rotate(notMapped);
+ }
+ }
+ // handle final adjacent node in current combination
+ else
+ {
+ var keys = Object.keys(mapped).sort();
+ mb.output[top] = { k: keys, m: mapped };
+
+ // optimize away mappings that are already too large
+ var _s = this.serializeMapping(mb.output);
+ if(s[dir] === null || _compareSerializations(_s, s[dir].s) <= 0)
+ {
+ var oldCount = mb.count;
+
+ // recurse into adjacent values
+ for(var i in keys)
+ {
+ var k = keys[i];
+ 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
+ if(s[dir] === null ||
+ (_compareSerializations(_s, s[dir].s) <= 0 &&
+ _s.length >= s[dir].s.length))
+ {
+ s[dir] = { s: _s, m: mb.mapping };
+ }
+ }
+ }
+};
+
+/**
* Computes the relation serialization for the given blank node IRI.
*
* @param s the serialization to update.
@@ -1616,53 +1761,50 @@
mb.mapped[iri] = true;
var top = mb.mapNode(iri);
- // copy original mapping builder, loop over adjacent values
+ // copy original mapping builder
var original = mb.copy();
- var values = this.edges[dir][iri].bnodes.slice();
- var loop = Math.max(1, values.length);
- for(var i = 0; i < loop; ++i)
+
+ // split adjacent bnodes on mapped and not-mapped
+ var adj = this.edges[dir][iri].bnodes;
+ var mapped = {};
+ var notMapped = [];
+ for(var i in adj)
+ {
+ if(adj[i].s in mb.mapping)
+ {
+ mapped[mb.mapping[adj[i].s]] = adj[i].s;
+ }
+ else
+ {
+ notMapped.push(adj[i]);
+ }
+ }
+
+ // TODO: ensure this optimization does not alter canonical order
+
+ // if the current bnode already has a serialization, reuse it
+ /*var hint = (iri in this.serializations) ?
+ this.serializations[iri][dir] : null;
+ if(hint !== null)
+ {
+ var hm = hint.m;
+ notMapped.sort(function(a, b)
+ {
+ return _compare(hm[a.s], hm[b.s]);
+ });
+ for(var i in notMapped)
+ {
+ mapped[mb.mapNode(notMapped[i].s)] = notMapped[i].s;
+ }
+ notMapped = [];
+ }*/
+
+ // loop over possible combinations
+ var combos = Math.max(1, notMapped.length);
+ for(var i = 0; i < combos; ++i)
{
var m = (i === 0) ? mb : original.copy();
-
- // map all edge nodes
- var tmp = [];
- for(var i2 in values)
- {
- tmp.push(m.mapNode(values[i2].s));
- }
- m.output[top] = tmp.sort();
- var oldCount = m.count;
-
- // optimize away mappings that are already too large
- var _s = _serializeMapping(m.output);
- if(s[dir] === null || _compareSerializations(_s, s[dir].s) <= 0)
- {
- // recurse into adjacent values
- for(var i2 in values)
- {
- // TODO: optimization: for each value, see if the value already
- // has a shortest serialization for the given direction that
- // can be reused
- this.serializeBlankNode(s, values[i2].s, m, dir);
- }
-
- // reserialize if more nodes were mapped
- if(m.count > oldCount)
- {
- _s = _serializeMapping(m.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: m.mapping };
- }
- }
-
- // rotate values
- _rotate(values);
+ this.serializeCombos(s, top, mb, dir, mapped, notMapped);
}
}
};
@@ -2463,4 +2605,11 @@
return new jsonld.Processor().frame(input, frame, options);
};
+/**
+ * Creates the JSON-LD default context.
+ *
+ * @return the JSON-LD default context.
+ */
+jsonld.createDefaultContext = _createDefaultContext;
+
})();