Fix test output (':' now indicates absolute IRI), update jsonld.js.
authorDave Longley <dlongley@digitalbazaar.com>
Wed, 18 Apr 2012 13:05:07 -0400
changeset 532 5afe57e01f8e
parent 531 735a98612a98
child 533 5e5c74a119b5
Fix test output (':' now indicates absolute IRI), update jsonld.js.
playground/jsonld.js
test-suite/tests/frame-0010-out.jsonld
--- a/playground/jsonld.js	Tue Apr 17 00:24:51 2012 -0400
+++ b/playground/jsonld.js	Wed Apr 18 13:05:07 2012 -0400
@@ -18,11 +18,12 @@
  * @param input the JSON-LD object to compact.
  * @param ctx the context to compact with.
  * @param [options] options to use:
+ *          [base] the base IRI to use.
  *          [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).
  *          [resolver(url, callback(err, jsonCtx))] the URL resolver to use.
- * @param callback(err, compacted) called once the operation completes.
+ * @param callback(err, compacted, merged) called once the operation completes.
  */
 jsonld.compact = function(input, ctx) {
   // get arguments
@@ -40,6 +41,9 @@
   }
 
   // set default options
+  if(!('base' in options)) {
+    options.base = '';
+  }
   if(!('strict' in options)) {
     options.strict = true;
   }
@@ -84,7 +88,7 @@
         // do compaction
         input = expanded;
         var compacted = new Processor().compact(ctx, null, input, options);
-        cleanup(null, compacted, options);
+        cleanup(null, compacted, ctx, options);
       }
       catch(ex) {
         callback(ex);
@@ -93,7 +97,7 @@
   });
 
   // performs clean up after compaction
-  function cleanup(err, compacted, options) {
+  function cleanup(err, compacted, merged, options) {
     if(err) {
       return callback(err);
     }
@@ -154,7 +158,7 @@
       }
     }
 
-    callback(null, compacted);
+    callback(null, compacted, merged);
   };
 };
 
@@ -163,6 +167,7 @@
  *
  * @param input the JSON-LD object to expand.
  * @param [options] the options to use:
+ *          [base] the base IRI to use.
  *          [resolver(url, callback(err, jsonCtx))] the URL resolver to use.
  * @param callback(err, expanded) called once the operation completes.
  */
@@ -178,6 +183,9 @@
   callback = arguments[callbackArg];
 
   // set default options
+  if(!('base' in options)) {
+    options.base = '';
+  }
   if(!('resolver' in options)) {
     options.resolver = jsonld.urlResolver;
   }
@@ -190,7 +198,7 @@
     }
     try {
       // do expansion
-      var expanded = new Processor().expand({}, null, input, false);
+      var expanded = new Processor().expand({}, null, input, options, false);
 
       // optimize away @graph with no other properties
       if(_isObject(expanded) && ('@graph' in expanded) &&
@@ -215,6 +223,7 @@
  * @param input the JSON-LD object to frame.
  * @param frame the JSON-LD frame to use.
  * @param [options] the framing options.
+ *          [base] the base IRI to use.
  *          [embed] default @embed flag (default: true).
  *          [explicit] default @explicit flag (default: false).
  *          [omitDefault] default @omitDefault flag (default: false).
@@ -233,6 +242,9 @@
   var callback = arguments[callbackArg];
 
   // set default options
+  if(!('base' in options)) {
+    options.base = '';
+  }
   if(!('resolver' in options)) {
     options.resolver = jsonld.urlResolver;
   }
@@ -272,22 +284,16 @@
 
       // compact result (force @graph option to true)
       options.graph = true;
-      jsonld.compact(framed, ctx, options, function(err, compacted) {
+      jsonld.compact(framed, ctx, options, function(err, compacted, merged) {
         if(err) {
           return callback(new JsonLdError(
             'Could not compact framed output.',
             'jsonld.FrameError', {cause: err}));
         }
         // get graph alias
-        var graph = '@graph';
-        for(var key in compacted) {
-          if(key !== '@context') {
-            graph = key;
-            break;
-          }
-        }
+        var graph = _compactIri(merged, '@graph');
         // remove @preserve from results
-        compacted[graph] = _removePreserve(compacted[graph]);
+        compacted[graph] = _removePreserve(merged, compacted[graph]);
         callback(null, compacted);
       });
     });
@@ -299,6 +305,7 @@
  *
  * @param input the JSON-LD object to normalize.
  * @param [options] the options to use:
+ *          [base] the base IRI to use.
  *          [resolver(url, callback(err, jsonCtx))] the URL resolver to use.
  * @param callback(err, normalized) called once the operation completes.
  */
@@ -314,6 +321,9 @@
   callback = arguments[callbackArg];
 
   // set default options
+  if(!('base' in options)) {
+    options.base = '';
+  }
   if(!('resolver' in options)) {
     options.resolver = jsonld.urlResolver;
   }
@@ -1076,17 +1086,19 @@
  * @param ctx the context to use.
  * @param property the property for the element, null for none.
  * @param element the element to expand.
+ * @param options the expansion options.
  * @param propertyIsList true if the property is a list, false if not.
  *
  * @return the expanded value.
  */
-Processor.prototype.expand = function(ctx, property, element, propertyIsList) {
+Processor.prototype.expand = function(
+  ctx, property, element, options, propertyIsList) {
   // recursively expand array
   if(_isArray(element)) {
     var rval = [];
     for(var i in element) {
       // expand element
-      var e = this.expand(ctx, property, element[i], propertyIsList);
+      var e = this.expand(ctx, property, element[i], options, propertyIsList);
       if(_isArray(e) && propertyIsList) {
         // lists of lists are illegal
         throw new JsonLdError(
@@ -1171,7 +1183,7 @@
       // recurse into @list, @set, or @graph, keeping the active property
       var isList = (prop === '@list');
       if(isList || prop === '@set' || prop === '@graph') {
-        value = this.expand(ctx, property, value, isList);
+        value = this.expand(ctx, property, value, options, isList);
         if(isList && _isListValue(value)) {
           throw new JsonLdError(
             'Invalid JSON-LD syntax; lists of lists are not permitted.',
@@ -1181,7 +1193,7 @@
       else {
         // update active property and recursively expand value
         property = key;
-        value = this.expand(ctx, property, value, false);
+        value = this.expand(ctx, property, value, options, false);
       }
 
       // drop null values if property is not @value (dropped below)
@@ -1257,7 +1269,7 @@
   }
 
   // expand element according to value expansion rules
-  return _expandValue(ctx, property, element);
+  return _expandValue(ctx, property, element, options.base);
 };
 
 /**
@@ -1555,16 +1567,20 @@
  * @param ctx the context to use.
  * @param property the property the value is associated with.
  * @param value the value to expand.
+ * @param base the base IRI to use.
  *
  * @return the expanded value.
  */
-function _expandValue(ctx, property, value) {
+function _expandValue(ctx, property, value, base) {
   // default to simple string return value
   var rval = value;
 
   // special-case expand @id and @type (skips '@id' expansion)
   var prop = _expandTerm(ctx, property);
-  if(prop === '@id' || prop === '@type') {
+  if(prop === '@id') {
+    rval = _expandTerm(ctx, value, base);
+  }
+  else if(prop === '@type') {
     rval = _expandTerm(ctx, value);
   }
   else {
@@ -1573,7 +1589,7 @@
 
     // do @id expansion
     if(type === '@id') {
-      rval = {'@id': _expandTerm(ctx, value)};
+      rval = {'@id': _expandTerm(ctx, value, base)};
     }
     // other type
     else if(type !== null) {
@@ -2460,20 +2476,23 @@
 /**
  * Removes the @preserve keywords as the last step of the framing algorithm.
  *
+ * @param ctx the context used to compact the input.
  * @param input the framed, compacted output.
  *
  * @return the resulting output.
  */
-function _removePreserve(input) {
+function _removePreserve(ctx, input) {
   // recurse through arrays
   if(_isArray(input)) {
+    var output = [];
     for(var i in input) {
-      input[i] = _removePreserve(input[i]);
+      var result = _removePreserve(ctx, input[i]);
+      // drop nulls from arrays
+      if(result !== null) {
+        output.push(result);
+      }
     }
-    // drop null-only arrays
-    if(input.length === 1 && input[0] === null) {
-      input = [];
-    }
+    input = output;
   }
   else if(_isObject(input)) {
     // remove @preserve
@@ -2491,13 +2510,19 @@
 
     // recurse through @lists
     if(_isListValue(input)) {
-      input['@list'] = _removePreserve(input['@list']);
+      input['@list'] = _removePreserve(ctx, input['@list']);
       return input;
     }
 
     // recurse through properties
     for(var prop in input) {
-      input[prop] = _removePreserve(input[prop]);
+      var result = _removePreserve(ctx, input[prop]);
+      var container = jsonld.getContextValue(ctx, prop, '@container');
+      if(_isArray(result) && result.length === 1 &&
+        container !== '@set' && container !== '@list') {
+        result = result[0];
+      }
+      input[prop] = result;
     }
   }
   return input;
@@ -2531,15 +2556,15 @@
  * @param container the specific @container to match or null.
  * @param result the resulting term or CURIE.
  * @param results the results array.
- * @param bestMatch the current bestMatch value.
+ * @param rank the current rank value.
  *
- * @return the new bestMatch value.
+ * @return the new rank value.
  */
-function _isBestMatch(ctx, key, value, container, result, results, bestMatch) {
+function _isBestMatch(ctx, key, value, container, result, results, rank) {
   // value is null, match any key
   if(value === null) {
     results.push(result);
-    return bestMatch;
+    return rank;
   }
 
   var valueIsList = _isListValue(value);
@@ -2558,42 +2583,42 @@
       (entry['@container'] === '@set' && container === null)) &&
     ((valueHasType && entryType === value['@type']) ||
     (!valueHasType && entry['@language'] === language))) {
-    if(bestMatch < 3) {
-      bestMatch = 3;
+    if(rank < 3) {
+      rank = 3;
       results.length = 0;
     }
     results.push(result);
   }
   // no container with type or language
-  else if(bestMatch < 3 &&
+  else if(rank < 3 &&
     !entryHasContainer && !valueIsList &&
     ((valueHasType && entryType === value['@type']) ||
       (!valueHasType && entry['@language'] === language))) {
-    if(bestMatch < 2) {
-      bestMatch = 2;
+    if(rank < 2) {
+      rank = 2;
       results.length = 0;
     }
     results.push(result);
   }
   // container with no type or language
-  else if(bestMatch < 2 &&
+  else if(rank < 2 &&
     entryHasContainer &&
     (entry['@container'] === container ||
       (entry['@container'] === '@set' && container === null)) &&
     !('@type' in entry) && !('@language' in entry)) {
-    if(bestMatch < 1) {
-      bestMatch = 1;
+    if(rank < 1) {
+      rank = 1;
       results.length = 0;
     }
     results.push(result);
   }
   // no container, no type, no language
-  else if(bestMatch < 1 &&
+  else if(rank < 1 &&
     !entryHasContainer && !('@type' in entry) && !('@language' in entry)) {
     results.push(result);
   }
 
-  return bestMatch;
+  return rank;
 }
 
 /**
@@ -2640,7 +2665,7 @@
   // check the context for terms that could shorten the IRI
   // (give preference to terms over prefixes)
   var terms = [];
-  var bestMatch = 0;
+  var rank = 0;
   for(var key in ctx) {
     // skip special context keys (start with '@')
     if(key.indexOf('@') === 0) {
@@ -2648,8 +2673,7 @@
     }
     // compact to a term
     if(iri === jsonld.getContextValue(ctx, key, '@id')) {
-      bestMatch = _isBestMatch(
-        ctx, key, value, container, key, terms, bestMatch);
+      rank = _isBestMatch(ctx, key, value, container, key, terms, rank);
     }
   }
 
@@ -2661,7 +2685,7 @@
 
   // term not found, check the context for a prefix
   var curies = [];
-  bestMatch = 0;
+  rank = 0;
   for(var key in ctx) {
     // skip special context keys (start with '@')
     if(key.indexOf('@') === 0) {
@@ -2675,8 +2699,7 @@
       var idx = iri.indexOf(ctxIri);
       if(idx === 0 && iri.length > ctxIri.length) {
         var curie = key + ':' + iri.substr(ctxIri.length);
-        bestMatch = _isBestMatch(
-          ctx, key, value, container, curie, curies, bestMatch);
+        rank = _isBestMatch(ctx, key, value, container, curie, curies, rank);
       }
     }
   }
@@ -2698,11 +2721,12 @@
  *
  * @param ctx the context to use.
  * @param term the term to expand.
+ * @param base the base IRI to use if a relative IRI is detected.
  * @param deep (used internally to recursively expand).
  *
  * @return the expanded term as an absolute IRI.
  */
-function _expandTerm(ctx, term, deep) {
+function _expandTerm(ctx, term, base, deep) {
   // nothing to expand
   if(term === null) {
     return null;
@@ -2754,9 +2778,14 @@
         cycles[rval] = true;
       }
       recurse = rval;
-      recurse = _expandTerm(ctx, recurse, true);
+      recurse = _expandTerm(ctx, recurse, base, true);
     }
     rval = recurse;
+
+    // apply base IRI to relative IRIs if provided
+    if(!_isAbsoluteIri(rval) && !_isKeyword(rval) && !_isUndefined(base)) {
+      rval = base + rval;
+    }
   }
 
   return rval;
@@ -3071,7 +3100,7 @@
  * @return true if the value is an absolute IRI, false if not.
  */
 function _isAbsoluteIri(value) {
-  return (/\w+:\/\/.+/).test(value);
+  return value.indexOf(':') !== -1;
 }
 
 /**
--- a/test-suite/tests/frame-0010-out.jsonld	Tue Apr 17 00:24:51 2012 -0400
+++ b/test-suite/tests/frame-0010-out.jsonld	Wed Apr 18 13:05:07 2012 -0400
@@ -10,6 +10,9 @@
   "@graph": [{
     "@id": "http://example.com/asset",
     "@type": "ps:Asset",
-    "dc:creator": null
+    "dc:creator": {
+      "@id": "_:t0",
+      "foaf:name": "John Doe"
+    }
   }]
 }
\ No newline at end of file