Update to latest jsonld.js from Forge.
authorDavid I. Lehn <dlehn@digitalbazaar.com>
Sun, 24 Jul 2011 23:23:00 -0400
changeset 75 1572c224411a
parent 72 4b0e9ddd3d6e
child 76 47a4cc7bf8b7
Update to latest jsonld.js from Forge.
playground/jsonld.js
--- 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;
+
 })();