Reversed bnode ordering and updated jsonld.js.
authorDave Longley <dlongley@digitalbazaar.com>
Fri, 25 Jan 2013 15:36:28 -0500
changeset 1158 141ae6f536c4
parent 1157 6f8aeaf2b8ab
child 1159 c4c22e67d1fb
Reversed bnode ordering and updated jsonld.js.
playground/jsonld.js
test-suite/tests/frame-0015-out.jsonld
--- a/playground/jsonld.js	Fri Jan 25 10:40:35 2013 -0500
+++ b/playground/jsonld.js	Fri Jan 25 15:36:28 2013 -0500
@@ -50,6 +50,8 @@
  *          [strict] use strict mode (default: true).
  *          [optimize] true to optimize the compaction (default: false).
  *          [graph] true to always output a top-level graph (default: false).
+ *          [skipExpansion] true to assume the input is expanded and skip
+ *            expansion, false not to, defaults to false.
  *          [resolver(url, callback(err, jsonCtx))] the URL resolver to use.
  * @param callback(err, compacted, ctx) called once the operation completes.
  */
@@ -81,12 +83,22 @@
   if(!('graph' in options)) {
     options.graph = false;
   }
+  if(!('skipExpansion' in options)) {
+    options.skipExpansion = false;
+  }
   if(!('resolver' in options)) {
     options.resolver = jsonld.urlResolver;
   }
 
+  var expand = function(input, options, callback) {
+    if(options.skipExpansion) {
+      return callback(null, input);
+    }
+    jsonld.expand(input, options, callback);
+  };
+
   // expand input then do compaction
-  jsonld.expand(input, options, function(err, expanded) {
+  expand(input, options, function(err, expanded) {
     if(err) {
       return callback(new JsonLdError(
         'Could not expand input before compaction.',
@@ -109,9 +121,8 @@
         }
 
         // do compaction
-        input = expanded;
         var compacted = new Processor().compact(
-          activeCtx, null, input, options);
+          activeCtx, null, expanded, options);
         cleanup(null, compacted, activeCtx, options);
       }
       catch(ex) {
@@ -316,8 +327,9 @@
       return callback(null, flattened);
     }
 
-    // compact result (force @graph option to true)
+    // compact result (force @graph option to true, skip expansion)
     options.graph = true;
+    options.skipExpansion = true;
     jsonld.compact(flattened, ctx, options, function(err, compacted, ctx) {
       if(err) {
         return callback(new JsonLdError(
@@ -400,8 +412,9 @@
         return callback(ex);
       }
 
-      // compact result (force @graph option to true)
+      // compact result (force @graph option to true, skip expansion)
       opts.graph = true;
+      opts.skipExpansion = true;
       jsonld.compact(framed, ctx, opts, function(err, compacted, ctx) {
         if(err) {
           return callback(new JsonLdError(
@@ -462,8 +475,9 @@
       return callback(ex);
     }
 
-    // compact result (force @graph option to true)
+    // compact result (force @graph option to true, skip expansion)
     options.graph = true;
+    options.skipExpansion = true;
     jsonld.compact(flattened, ctx, options, function(err, compacted, ctx) {
       if(err) {
         return callback(new JsonLdError(
@@ -799,6 +813,47 @@
 };
 
 /**
+ * Creates an active context cache.
+ *
+ * @param size the maximum size of the cache.
+ */
+jsonld.ActiveContextCache = function(size) {
+  this.order = [];
+  this.cache = {};
+  this.size = size || 100;
+};
+jsonld.ActiveContextCache.prototype.get = function(activeCtx, localCtx) {
+  var key1 = JSON.stringify(activeCtx);
+  var key2 = JSON.stringify(localCtx);
+  var level1 = this.cache[key1];
+  if(level1 && key2 in level1) {
+    return level1[key2];
+  }
+  return null;
+};
+jsonld.ActiveContextCache.prototype.set = function(
+  activeCtx, localCtx, result) {
+  if(this.order.length === this.size) {
+    var entry = this.order.shift();
+    delete this.cache[entry.activeCtx][entry.localCtx];
+  }
+  var key1 = JSON.stringify(activeCtx);
+  var key2 = JSON.stringify(localCtx);
+  this.order.push({activeCtx: key1, localCtx: key2});
+  if(!(key1 in this.cache)) {
+    this.cache[key1] = {};
+  }
+  this.cache[key1][key2] = result;
+};
+
+/**
+ * Default JSON-LD cache.
+ */
+jsonld.cache = {
+  activeCtx: new jsonld.ActiveContextCache()
+};
+
+/**
  * URL resolvers.
  */
 jsonld.urlResolvers = {};
@@ -2293,8 +2348,18 @@
  * @return the new active context.
  */
 Processor.prototype.processContext = function(activeCtx, localCtx, options) {
+  var rval = null;
+
+  // get context from cache if available
+  if(jsonld.cache.activeCtx) {
+    rval = jsonld.cache.activeCtx.get(activeCtx, localCtx);
+    if(rval) {
+      return rval;
+    }
+  }
+
   // initialize the resulting context
-  var rval = activeCtx.clone();
+  rval = activeCtx.clone();
 
   // normalize local context to an array of @context objects
   if(_isObject(localCtx) && '@context' in localCtx &&
@@ -2376,6 +2441,11 @@
     }
   }
 
+  // cache result
+  if(jsonld.cache.activeCtx) {
+    jsonld.cache.activeCtx.set(activeCtx, localCtx, rval);
+  }
+
   return rval;
 };
 
@@ -3295,7 +3365,10 @@
           if('@default' in next) {
             preserve = _clone(next['@default']);
           }
-          output[prop] = {'@preserve': preserve};
+          if(!_isArray(preserve)) {
+            preserve = [preserve];
+          }
+          output[prop] = [{'@preserve': [preserve]}];
         }
       }
 
@@ -4420,35 +4493,27 @@
     },
     namer: namer,
     inverse: null,
-    getInverse: function() {
-      if(this.inverse) {
-        return this.inverse;
-      }
-      this.inverse = _createInverseContext(this);
-      return this.inverse;
-    },
-    clone: function() {
-      var child = {};
-      child['@base'] = this['@base'];
-      child.keywords = _clone(this.keywords);
-      child.mappings = _clone(this.mappings);
-      child.namer = this.namer;
-      child.clone = this.clone;
-      child.inverse = null;
-      child.getInverse = this.getInverse;
-      return child;
-    }
+    getInverse: _createInverseContext,
+    clone: _cloneActiveContext
   };
 
   /**
-   * Generates an inverse context for use in the compaction algorithm.
+   * Generates an inverse context for use in the compaction algorithm, if
+   * not already generated for the given active context.
    *
    * @param activeCtx the active context to create the inverse context from.
    *
    * @return the inverse context.
    */
   function _createInverseContext(activeCtx) {
-    var inverse = {};
+    if(!activeCtx) {
+      activeCtx = this;
+    }
+    // lazily create inverse
+    if(activeCtx.inverse) {
+      return activeCtx.inverse;
+    }
+    var inverse = activeCtx.inverse = {};
 
     // handle default language
     var defaultLanguage = activeCtx['@language'] || '@none';
@@ -4559,6 +4624,23 @@
       }
     }
   }
+
+  /**
+   * Clones an active context, creating a child active context.
+   *
+   * @return a clone (child) of the active context.
+   */
+  function _cloneActiveContext() {
+    var child = {};
+    child['@base'] = this['@base'];
+    child.keywords = _clone(this.keywords);
+    child.mappings = _clone(this.mappings);
+    child.namer = this.namer;
+    child.clone = this.clone;
+    child.inverse = null;
+    child.getInverse = this.getInverse;
+    return child;
+  }
 }
 
 /**
--- a/test-suite/tests/frame-0015-out.jsonld	Fri Jan 25 10:40:35 2013 -0500
+++ b/test-suite/tests/frame-0015-out.jsonld	Fri Jan 25 15:36:28 2013 -0500
@@ -84,13 +84,13 @@
       "@type": "sp:VitalSigns",
       "sp:belongsTo": "http://localhost:7000/records/999888",
       "sp:bloodPressure": {
-        "@id": "_:t0",
+        "@id": "_:t2",
         "@type": "sp:BloodPressure",
         "sp:systolic": {
-          "@id": "_:t1",
+          "@id": "_:t0",
           "@type": "sp:VitalSign",
           "sp:vitalName": {
-            "@id": "_:t2",
+            "@id": "_:t1",
             "dcterms:title": "Systolic blood pressure",
             "@type": "sp:CodedValue",
             "sp:code": "http://loinc.org/codes/8480-6"
@@ -109,15 +109,15 @@
       "sp:hasStatement": "http://localhost:7000/records/999888/vital_signs/c9ddca3e-3df8-4f13-9a16-eecd80aa8ff6"
     },
     "sp:bloodPressure": {
-      "@id": "_:t0",
+      "@id": "_:t2",
       "@type": "sp:BloodPressure",
       "sp:systolic": {
-        "@id": "_:t1",
+        "@id": "_:t0",
         "@type": "sp:VitalSign",
         "sp:unit": "mm[Hg]",
         "sp:value": "111.226458141",
         "sp:vitalName": {
-          "@id": "_:t2",
+          "@id": "_:t1",
           "@type": "sp:CodedValue",
           "dcterms:title": "Systolic blood pressure",
           "sp:code": "http://loinc.org/codes/8480-6"