Merge branch 'master' of github.com:json-ld/json-ld.org
authorManu Sporny <msporny@digitalbazaar.com>
Mon, 21 May 2012 22:34:33 -0400
changeset 668 0bd98bbc5ed6
parent 661 675e86fbcd27 (current diff)
parent 667 558e03e1c0df (diff)
child 669 e903b19a84f5
Merge branch 'master' of github.com:json-ld/json-ld.org
--- a/playground/jsonld.js	Mon May 21 22:34:05 2012 -0400
+++ b/playground/jsonld.js	Mon May 21 22:34:33 2012 -0400
@@ -1365,12 +1365,16 @@
   // create framing state
   var state = {
     options: options,
-    subjects: {}
+    graphs: {'@default': {}, '@merged': {}}
   };
 
-  // produce a map of all subjects and name each bnode
+  // produce a map of all graphs and name each bnode
   var namer = new UniqueNamer('_:t');
-  _flatten(state.subjects, input, namer);
+  _flatten(input, state.graphs, '@default', namer);
+  namer = new UniqueNamer('_:t');
+  _flatten(input, state.graphs, '@merged', namer);
+  // FIXME: currently uses subjects from @merged graph only
+  state.subjects = state.graphs['@merged'];
 
   // frame the subjects
   var framed = [];
@@ -2295,87 +2299,103 @@
 /**
  * Recursively flattens the subjects in the given JSON-LD expanded input.
  *
- * @param subjects a map of subject @id to subject.
  * @param input the JSON-LD expanded input.
+ * @param graphs a map of graph name to subject map.
+ * @param graph the name of the current graph.
  * @param namer the blank node namer.
  * @param name the name assigned to the current input if it is a bnode.
  * @param list the list to append to, null for none.
  */
-function _flatten(subjects, input, namer, name, list) {
+function _flatten(input, graphs, graph, namer, name, list) {
   // recurse through array
   if(_isArray(input)) {
     for(var i in input) {
-      _flatten(subjects, input[i], namer, undefined, list);
-    }
-  }
-  // handle subject
-  else if(_isObject(input)) {
-    // add value to list
-    if(_isValue(input) && list) {
-      list.push(input);
-      return;
+      _flatten(input[i], graphs, graph, namer, undefined, list);
     }
-
-    // get name for subject
-    if(_isUndefined(name)) {
-      name = _isBlankNode(input) ? namer.getName(input['@id']) : input['@id'];
-    }
-
-    // add subject reference to list
+    return;
+  }
+
+  // add non-object or value to list
+  if(!_isObject(input) || _isValue(input)) {
     if(list) {
-      list.push({'@id': name});
+      list.push(input);
     }
-
-    // create new subject or merge into existing one
-    var subject = subjects[name] = subjects[name] || {};
-    subject['@id'] = name;
-    for(var prop in input) {
-      // skip @id
-      if(prop === '@id') {
-        continue;
+    return;
+  }
+
+  // Note: At this point, input must be a subject.
+
+  // get name for subject
+  if(_isUndefined(name)) {
+    name = _isBlankNode(input) ? namer.getName(input['@id']) : input['@id'];
+  }
+
+  // add subject reference to list
+  if(list) {
+    list.push({'@id': name});
+  }
+
+  // create new subject or merge into existing one
+  var subjects = graphs[graph];
+  var subject = subjects[name] = subjects[name] || {};
+  subject['@id'] = name;
+  var props = Object.keys(input).sort();
+  for(var p in props) {
+    var prop = props[p];
+
+    // skip @id
+    if(prop === '@id') {
+      continue;
+    }
+
+    // recurse into graph
+    if(prop === '@graph') {
+      // add graph subjects map entry
+      if(!(name in graphs)) {
+        graphs[name] = {};
       }
-
-      // copy keywords
-      if(_isKeyword(prop)) {
-        subject[prop] = input[prop];
-        continue;
+      var g = (graph === '@merged') ? graph : name;
+      _flatten(input[prop], graphs, g, namer);
+      continue;
+    }
+
+    // copy non-@type keywords
+    if(prop !== '@type' && _isKeyword(prop)) {
+      subject[prop] = input[prop];
+      continue;
+    }
+
+    // iterate over objects
+    var objects = input[prop];
+    for(var i in objects) {
+      var o = objects[i];
+
+      // handle embedded subject or subject reference
+      if(_isSubject(o) || _isSubjectReference(o)) {
+        // rename blank node @id
+        var id = _isBlankNode(o) ? namer.getName(o['@id']) : o['@id'];
+
+        // add reference and recurse
+        jsonld.addValue(subject, prop, {'@id': id}, true);
+        _flatten(o, graphs, graph, namer, id);
       }
-
-      // iterate over objects
-      var objects = input[prop];
-      for(var i in objects) {
-        var o = objects[i];
-
-        // handle embedded subject or subject reference
-        if(_isSubject(o) || _isSubjectReference(o)) {
-          // rename blank node @id
-          var id = ('@id' in o) ? o['@id'] : '_:';
-          if(id.indexOf('_:') === 0) {
-            id = namer.getName(id);
-          }
-
-          // add reference and recurse
-          jsonld.addValue(subject, prop, {'@id': id}, true);
-          _flatten(subjects, o, namer, id, null);
+      else {
+        // recurse into list
+        if(_isList(o)) {
+          var _list = [];
+          _flatten(o['@list'], graphs, graph, namer, name, _list);
+          o = {'@list': _list};
         }
-        else {
-          // recurse into list
-          if(_isList(o)) {
-            var l = [];
-            _flatten(subjects, o['@list'], namer, name, l);
-            o = {'@list': l};
-          }
-
-          // add non-subject
-          jsonld.addValue(subject, prop, o, true);
+        // special-handle @type IRIs
+        else if(prop === '@type' && o.indexOf('_:') === 0) {
+          o = namer.getName(o);
         }
+
+        // add non-subject
+        jsonld.addValue(subject, prop, o, true);
       }
     }
   }
-  // add non-object to list
-  else if(list) {
-    list.push(input);
-  }
 }
 
 /**
@@ -3972,7 +3992,13 @@
       s.object = {nominalValue: match[5], interfaceName: 'BlankNode'};
     }
     else {
-      s.object = {nominalValue: match[6], interfaceName: 'LiteralNode'};
+      var unescaped = match[6]
+        .replace(/\\"/g, '"')
+        .replace(/\\t/g, '\t')
+        .replace(/\\n/g, '\n')
+        .replace(/\\r/g, '\r')
+        .replace(/\\\\/g, '\\');
+      s.object = {nominalValue: unescaped, interfaceName: 'LiteralNode'};
       if(!_isUndefined(match[7])) {
         s.object.datatype = {nominalValue: match[7], interfaceName: 'IRI'};
       }
@@ -4047,7 +4073,13 @@
     }
   }
   else {
-    quad += '"' + o.nominalValue + '"';
+    var escaped = o.nominalValue
+      .replace(/\\/g, '\\\\')
+      .replace(/\t/g, '\\t')
+      .replace(/\n/g, '\\n')
+      .replace(/\r/g, '\\r')
+      .replace(/\"/g, '\\"');
+    quad += '"' + escaped + '"';
     if('datatype' in o) {
       quad += '^^<' + o.datatype.nominalValue + '>';
     }
--- a/playground/playground.js	Mon May 21 22:34:05 2012 -0400
+++ b/playground/playground.js	Mon May 21 22:34:33 2012 -0400
@@ -124,7 +124,7 @@
     var startTab = getParameterByName('startTab');
     if(startTab) {
         // strip 'tab-' to get the tab's panel's I D
-        $('#tabs').tabs("select", "#"+startTab.substr(4));
+        $('#tabs').tabs('select', '#' + startTab.substr(4));
     }
 
     // wait for ajax if needed
@@ -205,8 +205,8 @@
         if(err) {
           return callback(err);
         }
-        $('#normalized').html(js_beautify(JSON.stringify(normalized)),
-          {'indent_size': 2, 'brace_style': 'expand'});
+        $('#normalized').html(js_beautify(JSON.stringify(normalized),
+          {'indent_size': 2, 'brace_style': 'expand'}));
         callback();
       });
     }
@@ -215,8 +215,8 @@
         if(err) {
           return callback(err);
         }
-        $('#expanded').html(js_beautify(JSON.stringify(expanded)),
-          {'indent_size': 2, 'brace_style': 'expand'});
+        $('#expanded').html(js_beautify(JSON.stringify(expanded),
+          {'indent_size': 2, 'brace_style': 'expand'}));
         callback();
       });
     }
@@ -225,8 +225,8 @@
         if(err) {
           return callback(err);
         }
-        $('#compacted').html(js_beautify(JSON.stringify(compacted)),
-          {'indent_size': 2, 'brace_style': 'expand'});
+        $('#compacted').html(js_beautify(JSON.stringify(compacted),
+          {'indent_size': 2, 'brace_style': 'expand'}));
         callback();
       });
     }
@@ -235,8 +235,8 @@
         if(err) {
           return callback(err);
         }
-        $('#framed').html(js_beautify(JSON.stringify(framed)),
-          {'indent_size': 2, 'brace_style': 'expand'});
+        $('#framed').html(js_beautify(JSON.stringify(framed),
+          {'indent_size': 2, 'brace_style': 'expand'}));
         callback();
       });
     }
@@ -279,25 +279,27 @@
     }
 
     // If we're using a param, check to see if it is valid JSON
-    var needParam = false, toValidate = null;
+    var needParam = false;
+    var param = null;
+    var jsonParam = null;
 
-    if (playground.activeTab === 'tab-compacted') {
-        toValidate = $('#context').val();
-        needParam = true;
+    if(playground.activeTab === 'tab-compacted') {
+      jsonParam = $('#context').val();
+      needParam = true;
     }
-    else if (playground.activeTab === 'tab-framed') {
-        toValidate = $('#frame').val();
-        needParam = true;
+    else if(playground.activeTab === 'tab-framed') {
+      jsonParam = $('#frame').val();
+      needParam = true;
     }
 
-    if (needParam) {
-        try {
-          var param = JSON.parse(toValidate);
-        }
-        catch(e) {
-          $('#param-errors').text($('#param-type').text() + ' - ' + e);
-          errors = true;
-        }
+    if(needParam) {
+      try {
+        param = JSON.parse(jsonParam);
+      }
+      catch(e) {
+        $('#param-errors').text($('#param-type').text() + ' - ' + e);
+        errors = true;
+      }
     }
 
     // errors detected
@@ -320,12 +322,10 @@
       // generate a link for current data
       var link = '?json-ld=' + encodeURIComponent(JSON.stringify(input));
       if($('#frame').val().length > 0) {
-        link += '&frame=' +
-            encodeURIComponent($("#frame").val());
+        link += '&frame=' + encodeURIComponent($('#frame').val());
       }
       if($('#context').val().length > 0) {
-        link += '&context=' +
-            encodeURIComponent($("#context").val());
+        link += '&context=' + encodeURIComponent($('#context').val());
       }
 
       // Start at the currently active tab
@@ -378,8 +378,8 @@
 
   /**
    * Populate the UI with markup, frame, and context JSON. The data parameter
-   * should have a 'markup' field and optional 'frame' and 'context' fields that
-   * contain a serialized JSON string.
+   * should have a 'markup' field and optional 'frame' and 'context' fields
+   * that contain a serialized JSON string.
    *
    * @param data object with optional 'markup', 'frame' and 'context' fields.
    */
@@ -449,7 +449,7 @@
       }
       else if('@context' in playground.examples[name]) {
         // use context from markup as default
-        var ctx = playground.examples[name]['@context']
+        var ctx = playground.examples[name]['@context'];
         data.context = JSON.stringify(ctx);
       }
     }
--- a/spec/latest/json-ld-api/index.html	Mon May 21 22:34:05 2012 -0400
+++ b/spec/latest/json-ld-api/index.html	Mon May 21 22:34:33 2012 -0400
@@ -1868,82 +1868,76 @@
 <h3>Subject Map Generation</h3>
 <p>The Subject Map Generation algorithm takes as input an expanded JSON-LD document and results in a <tref>JSON object</tref>
   <em>subjectMap</em> holding a flat representation of the graphs and nodes represented in the document. All nodes that are not
-  uniquelly identified by an IRI get assigned a (new) <tref>blank node</tref> identifier. The resulting <em>subjectMap</em>
+  uniquely identified by an IRI get assigned a (new) <tref>blank node</tref> identifier. The resulting <em>subjectMap</em>
   document will have a property for every graph in the document whose value is another object with a property for every
   node represented in the document. While the default graph is stored under the <code>@default</code> property and the merged graph
   under the <code>@merged</code> property, all other graphs are stored under their respective <tref title="IRI">IRIs</tref>.</p>
 
-<p>The algorithm takes as input the initially empty <em>subjectMap</em>, the expanded JSON-LD document as <em>element</em>,
-  <code>null</code> as <em>parent</em>, <code>false</code> for the <em>list</em> flag, <code>false</code> for the <em>iri</em> flag,
-  and <code>@default</code> as <em>graph</em>.</p>
-
-<p class="issue">If the values of <code>@type</code> would be expanded as everything else to <code>@id</code> form as proposed in
-  <a href="https://github.com/json-ld/json-ld.org/issues/120">ISSUE-120</a>, the <em>iri</em> flag could be eliminated.</p>
+<p>The algorithm takes as input the expanded JSON-LD document as <em>element</em>, the initially empty <em>subjectMap</em>,
+  <code>@default</code> as <em>graph</em>, and <tref>null</tref> as <em>list</em>.</p>
 
 <ol class="algorithm">
-  <li>If <em>element</em> is an array, process each entry in <em>element</em> recursively, using this algorithm.</li>
-  <li>Otherwise, if <em>element</em> is a <tref>JSON object</tref> without a <code>@value</code> property:
+  <li>If <em>element</em> is an array, process each entry in <em>element</em> recursively, using this algorithm
+    and return.</li>
+  <li>If <em>element</em> is not a <tref>JSON object</tref> or if it has a <code>@value</code> property,
+    then if <em>list</em> is not <tref>null</tref>, append <em>element</em> to <em>list</em> and return.
+  </li>
+  <li>If the <code>@id</code> property exists and is an <tref>IRI</tref>, set <em>id</em> to its value, otherwise
+    set it to a <tref>blank node</tref> identifer created by the
+    <a href="#generate-blank-node-identifier">Generate Blank Node Identifier</a> algorithm.
+  </li>
+  <li>If <em>list</em> is not <tref>null</tref>, append a new <tref>subject reference</tref> to <em>list</em> using
+    <em>id</em> at the value for <code>@id</code>.
+  </li>
+  <li>Let <em>subjects</em> be the value in <em>subjectMap</em> where the key is <em>graph</em>; if no such
+    value exists, insert a new <tref>JSON object</tref> for the key <em>graph</em>. If <em>id</em> is not in
+    <em>subjects</em>, create a new <tref>JSON object</tref> <em>subject</em> with <em>id</em> as the value
+    for <code>@id</code>. Let <em>subject</em> be the value of <em>id</em> in <em>subjects</em>.</li>
+  </li>
+  <li>For each <em>property</em> that is not <code>@id</code> and each <em>value</em> in <em>element</em> ordered
+    by <em>property</em>:
     <ol class="algorithm">
-      <li>If <em>element</em> has a <code>@list</code> property, create a new <tref>JSON object</tref> <em>flattenedList</em> with a
-        <code>@list</code> property whose value is an empty array and recursively call this algorithm by passing <em>subjectMap</em>,
-        the value of the <code>@list</code> property of <em>element</em> as <em>element</em>, <em>flattenedList</em> as <em>parent</em>,
-        <code>true</code> for the <em>list</em> flag, <code>false</code> for the <em>iri</em> flag and <em>graph</em>. Add <em>flattenedList</em>
-        to <em>parent</em> and return.</li>
-      <li>If the property <code>@id</code> is not an <tref>IRI</tref> and <em>graph</em> is not <code>@merge</code> or if the <code>@id</code>
-        property does not exist, set <em>id</em> to a <tref>blank node</tref> identifer created by the
-        <a href="#generate-blank-node-identifier">Generate Blank Node Identifier</a> algorithm; otherwise use the value of the <code>@id</code>
-        property as <em>id</em>.</li>
-      <li>If <em>parent</em> is not <tref>null</tref>, create new object with an <code>@id</code> property whose value equals <em>id</em> and
-        add it to <em>parent</em> if it doesn't exist yet in parent or the <em>list</em> flag is set to <code>true</code>.</li>
-      <li>If there already exists a <em>id</em> property in the <tref>JSON object</tref> of the <em>graph</em> property of <em>subjectMap</em>, set
-        <em>subject</em> to the value of that property.</li>
-      <li>Otherwise, create a new object with an <code>@id</code> property whose value equals <em>id</em> and add it as value of the <em>id</em>
-        property of the <tref>JSON object</tref> at the <em>graph</em> property of <em>subjectMap</em>.</li>
-      <li>For each <em>property</em> and <em>value</em> in <em>element</em> other than <code>@id</code> ordered by <em>property</em>:
+      <li>If <em>property</em> is <code>@graph</code>, recursively call this algorithm passing <em>value</em>
+        for <em>element</em>, <em>subjectMap</em>, <tref>null</tref> for <em>list</em> and if <em>graph</em>
+        is <code>merged</code> use <em>graph</em>, otherwise use <em>id</em> for <em>graph</em> and then continue.
+      </li>
+      <li>If <em>property</em> is not <code>@type</code> and is a keyword, merge <code>property</code> and
+        <code>value</code> into <code>subject</code> and then continue.
+      </li>
+      <li>For each value <em>v</em> in the array <em>value</em>:
         <ol class="algorithm">
-          <li>If <em>property</em> is <code>@type</code>, create a new property <code>@type</code> in <em>subject</em> whose value is an empty
-            array and recursively call this algorithm by passing <em>subjectMap</em>, <em>value</em> as <em>element</em>, the <code>@type</code>
-            property of <em>subject</em> as <em>parent</em>, <code>false</code> for the <em>list</em> flag, <code>true</code> for the <em>iri</em>
-            flag and <em>graph</em>. Then continue with the next property from <em>element</em>.</li>
-          <li>If <em>property</em> is <code>@graph</code>, recursively call this algorithm by passing <em>subjectMap</em>, <em>value</em> as
-            <em>element</em>, <tref>null</tref> for <em>parent</em>, <code>false</code> for the <em>list</em> flag, <code>false</code> for the
-            <em>iri</em> flag and <em>id</em> as <em>graph</em>. Then continue with the next property from <em>element</em>.</li>
-          <li>If <em>property</em> is a <a href="#syntax-tokens-and-keywords">keyword</a>, merge <em>property</em> and <em>value</em> into
-            <em>subject</em>.</li>
-          <li>Otherwise, if no <em>property</em> property exists yet in <em>subject</em> create one and initialize it with an empty array. Then
-            recursively call this algorithm by passing <em>subjectMap</em>, <em>value</em> as <em>element</em>, the <em>property</em>
-            property of <em>subject</em> as <em>parent</em>, <code>false</code> for the <em>list</em> flag, <code>false</code> for the <em>iri</em>
-            flag and <em>graph</em>.</li>
+          <li>If <em>v</em> is a <tref>subject definition</tref> or <tref>subject reference</tref>:
+            <ol class="algorithm">
+              <li>If the property <code>@id</code> is not an <tref>IRI</tref> or it does not exist,
+                map <em>v</em> to a <a href="#generate-blank-node-identifier">new blank node identifier</a>
+                to avoid collisions. If one does not already exist, add a <tref>subject reference</tref> for
+                <em>v</em> into <em>subject</em> for <em>property</em>.
+              </li>
+              <li>Recursively call this algorithm passing <em>v</em> for <em>value</em>, <em>subjectMap</em>,
+                <em>graph</em>, and <tref>null</tref> for <em>list</em>.
+              </li>
+            </ol>
+          </li>
+          <li>Otherwise if <em>v</em> has the property <code>@list</code> then recursively call this algorithm
+            with the value of <code>@list</code> as <em>element</em>, <em>subjectMap</em>, <em>graph</em>, and
+            a new array <em>flattenedList</em> as <em>list</em>. Create a new <tref>JSON object</tref> with the
+            property <code>@list</code> set to <em>flattenedList</em> and add it to <em>subject</em> for
+            <em>property</em>.
+          </li>
+          <li>Otherwise, if <em>property</em> is <code>@type</code> and <em>v</em> is not an <tref>IRI</tref>,
+            generate a <a href="#generate-blank-node-identifier">new blank node identifier</a> and add it
+            to <em>subject</em> for <em>property</em>.
+          </li>
+          <li>Otherwise, add <em>v</em> to <em>subject</em> for <em>property</em>.</li>
         </ol>
       </li>
     </ol>
   </li>
-  <li>Otherwise
-    <ol class="algorithm">
-      <li>If <em>element</em> is a string that begins with the characters <code>_:</code> and the <em>iri</em> flag is set to <code>true</code>,
-        map <em>element</em> to a <a href="#generate-blank-node-identifier">new blank node identifier</a> to avoid collisions.</li>
-      <li>If the <em>list</em> flag is set to <code>false</code>, check if <em>element</em> is already in <em>parent</em>. If so, return.</li>
-      <li>Add <em>element</em> to <em>parent.</em></li>
-    </ol>
-  </li>
 </ol>
 
 <p>After the above outlined algorithm has been executed, the subject map for all graphs including the default graph are contained in
-  <em>subjectMap</em>. To also create the subject map for the merged graph, perform the following steps:</p>
-
-<ol class="algorithm">
-  <li>Foreach property <em>graph</em> and value <em>graphMap</em> in <em>subjectMap</em>:
-    <ol class="algorithm">
-      <li>Foreach property <em>subject</em> and value <em>value</em> in <em>graphMap</em>:
-        <ol class="algorithm">
-          <li>Call the algorithm outlined above by passing <em>subjectMap</em>, <em>value</em> as <em>element</em>, <tref>null</tref> as
-            <em>parent</em>, <code>false</code> for the <em>list</em> flag, <code>false</code> for the <em>iri</em> flag and
-            <code>@merged</code> for <em>graph</em>.</li>
-        </ol>
-      </li>
-    </ol>
-  </li>
-</ol>
+  <em>subjectMap</em>. To also create the subject map for the merged graph, execute the algorithm again, but pass <code>merged</code>
+  for <em>graph</em>.
 
 </section>
 
--- a/test-suite/tests/compact-0001-context.jsonld	Mon May 21 22:34:05 2012 -0400
+++ b/test-suite/tests/compact-0001-context.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -1,3 +1,2 @@
 {
-  "@context": {}
-}
\ No newline at end of file
+}
--- a/test-suite/tests/compact-0003-context.jsonld	Mon May 21 22:34:05 2012 -0400
+++ b/test-suite/tests/compact-0003-context.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -1,3 +1,2 @@
 {
-  "@context": {}
-}
\ No newline at end of file
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/expand-0027-in.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,9 @@
+{
+  "@context": {
+    "mylist": {"@id": "http://example.com/mylist", "@container": "@list"},
+    "myset": {"@id": "http://example.com/myset", "@container": "@set"}
+  },
+  "@id": "http://example.org/id",
+  "mylist": [1, 2, 2, 3],
+  "myset": [1, 2, 2, 3]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/expand-0027-out.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,16 @@
+[{
+  "@id": "http://example.org/id",
+  "http://example.com/mylist": [{
+    "@list": [
+      {"@value": 1},
+      {"@value": 2},
+      {"@value": 2},
+      {"@value": 3}
+    ]
+  }],
+  "http://example.com/myset": [
+    {"@value": 1},
+    {"@value": 2},
+    {"@value": 3}
+  ]
+}]
--- a/test-suite/tests/expand-manifest.jsonld	Mon May 21 22:34:05 2012 -0400
+++ b/test-suite/tests/expand-manifest.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -134,5 +134,10 @@
     "name": "Expanding term mapping to @type uses @type syntax",
     "input": "expand-0026-in.jsonld",
     "expect": "expand-0026-out.jsonld"
+  }, {
+    "@type": ["test:TestCase", "jld:ExpandTest"],
+    "name": "Keep duplicate values in @list, remove from @set",
+    "input": "expand-0027-in.jsonld",
+    "expect": "expand-0027-out.jsonld"
   }]
 }
--- a/test-suite/tests/frame-0019-frame.jsonld	Mon May 21 22:34:05 2012 -0400
+++ b/test-suite/tests/frame-0019-frame.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -3,4 +3,4 @@
     "ex": "http://example.org/terms#"
   },
   "@type": "ex:Node"
-}
+}
\ No newline at end of file
--- a/test-suite/tests/frame-0019-in.jsonld	Mon May 21 22:34:05 2012 -0400
+++ b/test-suite/tests/frame-0019-in.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -17,4 +17,4 @@
     "ex:sees": "ex:node1",
     "ex:color": "red"
   }]
-}
+}
\ No newline at end of file
--- a/test-suite/tests/frame-0019-out.jsonld	Mon May 21 22:34:05 2012 -0400
+++ b/test-suite/tests/frame-0019-out.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -27,5 +27,4 @@
             "ex:color": "blue"
         }
     }]
-}
-
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/frame-0020-frame.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,1 @@
+{}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/frame-0020-in.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,34 @@
+{
+  "@context": {
+    "name": "http://rdf.data-vocabulary.org/#name",
+    "ingredient": "http://rdf.data-vocabulary.org/#ingredients",
+    "yield": "http://rdf.data-vocabulary.org/#yield",
+    "instructions": "http://rdf.data-vocabulary.org/#instructions",
+    "step": {
+      "@id": "http://rdf.data-vocabulary.org/#step",
+      "@type": "xsd:integer"
+    },
+    "description": "http://rdf.data-vocabulary.org/#description",
+    "xsd": "http://www.w3.org/2001/XMLSchema#"
+  },
+  "name": "Mojito",
+  "ingredient": ["12 fresh mint leaves", "1/2 lime, juiced with pulp", "1 tablespoons white sugar", "1 cup ice cubes", "2 fluid ounces white rum", "1/2 cup club soda"],
+  "yield": "1 cocktail",
+  "instructions": [
+  {
+    "step": 1,
+    "description": "Crush lime juice, mint and sugar together in glass."
+  }, {
+    "step": 2,
+    "description": "Fill glass to top with ice cubes."
+  }, {
+    "step": 3,
+    "description": "Pour white rum over ice."
+  }, {
+    "step": 4,
+    "description": "Fill the rest of glass with club soda, stir."
+  }, {
+    "step": 5,
+    "description": "Garnish with a lime wedge."
+  }]
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/frame-0020-out.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,80 @@
+{
+  "@graph": [
+  {
+    "@id": "_:t0",
+    "http://rdf.data-vocabulary.org/#ingredients": ["12 fresh mint leaves", "1/2 lime, juiced with pulp", "1 tablespoons white sugar", "1 cup ice cubes", "2 fluid ounces white rum", "1/2 cup club soda"],
+    "http://rdf.data-vocabulary.org/#instructions": [{
+      "@id": "_:t1",
+      "http://rdf.data-vocabulary.org/#step": {
+        "@value": 1,
+        "@type": "http://www.w3.org/2001/XMLSchema#integer"
+      },
+      "http://rdf.data-vocabulary.org/#description": "Crush lime juice, mint and sugar together in glass."
+    }, {
+      "@id": "_:t2",
+      "http://rdf.data-vocabulary.org/#step": {
+        "@value": 2,
+        "@type": "http://www.w3.org/2001/XMLSchema#integer"
+      },
+      "http://rdf.data-vocabulary.org/#description": "Fill glass to top with ice cubes."
+    }, {
+      "@id": "_:t3",
+      "http://rdf.data-vocabulary.org/#step": {
+        "@value": 3,
+        "@type": "http://www.w3.org/2001/XMLSchema#integer"
+      },
+      "http://rdf.data-vocabulary.org/#description": "Pour white rum over ice."
+    }, {
+      "@id": "_:t4",
+      "http://rdf.data-vocabulary.org/#step": {
+        "@value": 4,
+        "@type": "http://www.w3.org/2001/XMLSchema#integer"
+      },
+      "http://rdf.data-vocabulary.org/#description": "Fill the rest of glass with club soda, stir."
+    }, {
+      "@id": "_:t5",
+      "http://rdf.data-vocabulary.org/#step": {
+        "@value": 5,
+        "@type": "http://www.w3.org/2001/XMLSchema#integer"
+      },
+      "http://rdf.data-vocabulary.org/#description": "Garnish with a lime wedge."
+    }],
+    "http://rdf.data-vocabulary.org/#name": "Mojito",
+    "http://rdf.data-vocabulary.org/#yield": "1 cocktail"
+  }, {
+    "@id": "_:t1",
+    "http://rdf.data-vocabulary.org/#step": {
+      "@value": 1,
+      "@type": "http://www.w3.org/2001/XMLSchema#integer"
+    },
+    "http://rdf.data-vocabulary.org/#description": "Crush lime juice, mint and sugar together in glass."
+  }, {
+    "@id": "_:t2",
+    "http://rdf.data-vocabulary.org/#step": {
+      "@value": 2,
+      "@type": "http://www.w3.org/2001/XMLSchema#integer"
+    },
+    "http://rdf.data-vocabulary.org/#description": "Fill glass to top with ice cubes."
+  }, {
+    "@id": "_:t3",
+    "http://rdf.data-vocabulary.org/#step": {
+      "@value": 3,
+      "@type": "http://www.w3.org/2001/XMLSchema#integer"
+    },
+    "http://rdf.data-vocabulary.org/#description": "Pour white rum over ice."
+  }, {
+    "@id": "_:t4",
+    "http://rdf.data-vocabulary.org/#step": {
+      "@value": 4,
+      "@type": "http://www.w3.org/2001/XMLSchema#integer"
+    },
+    "http://rdf.data-vocabulary.org/#description": "Fill the rest of glass with club soda, stir."
+  }, {
+    "@id": "_:t5",
+    "http://rdf.data-vocabulary.org/#step": {
+      "@value": 5,
+      "@type": "http://www.w3.org/2001/XMLSchema#integer"
+    },
+    "http://rdf.data-vocabulary.org/#description": "Garnish with a lime wedge."
+  }]
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/frame-0021-frame.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,6 @@
+{
+  "@context": {
+    "dc": "http://purl.org/dc/elements/1.1/",
+    "ex": "http://example.org/vocab#"
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/frame-0021-in.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,31 @@
+{
+  "@context": {
+    "dc": "http://purl.org/dc/elements/1.1/",
+    "ex": "http://example.org/vocab#",
+    "xsd": "http://www.w3.org/2001/XMLSchema#",
+    "ex:contains": {
+      "@type": "@id"
+    }
+  },
+  "@graph": [
+  {
+    "@id": "_:Book",
+    "dc:label": "Book type"
+  }, {
+    "@id": "http://example.org/library",
+    "@type": "ex:Library",
+    "ex:contains": "http://example.org/library/the-republic"
+  }, {
+    "@id": "http://example.org/library/the-republic",
+    "@type": "_:Book",
+    "dc:creator": "Plato",
+    "dc:title": "The Republic",
+    "ex:contains": "http://example.org/library/the-republic#introduction"
+  }, {
+    "@id": "http://example.org/library/the-republic#introduction",
+    "@type": "ex:Chapter",
+    "dc:description": "An introductory chapter on The Republic.",
+    "dc:title": "The Introduction",
+    "dc:list": [1, 2, 3, 4, 4, 4, 5]
+  }]
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-suite/tests/frame-0021-out.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -0,0 +1,45 @@
+{
+  "@context": {
+    "dc": "http://purl.org/dc/elements/1.1/",
+    "ex": "http://example.org/vocab#"
+  },
+  "@graph": [
+  {
+    "@id": "_:t0",
+    "dc:label": "Book type"
+  }, {
+    "@id": "http://example.org/library",
+    "@type": "ex:Library",
+    "ex:contains": {
+      "@id": "http://example.org/library/the-republic",
+      "@type": "_:t0",
+      "dc:creator": "Plato",
+      "dc:title": "The Republic",
+      "ex:contains": {
+        "@id": "http://example.org/library/the-republic#introduction",
+        "@type": "ex:Chapter",
+        "dc:description": "An introductory chapter on The Republic.",
+        "dc:title": "The Introduction",
+        "dc:list": [1, 2, 3, 4, 5]
+      }
+    }
+  }, {
+    "@id": "http://example.org/library/the-republic",
+    "@type": "_:t0",
+    "ex:contains": {
+      "@id": "http://example.org/library/the-republic#introduction",
+      "@type": "ex:Chapter",
+      "dc:description": "An introductory chapter on The Republic.",
+      "dc:title": "The Introduction",
+      "dc:list": [1, 2, 3, 4, 5]
+    },
+    "dc:creator": "Plato",
+    "dc:title": "The Republic"
+  }, {
+    "@id": "http://example.org/library/the-republic#introduction",
+    "@type": "ex:Chapter",
+    "dc:description": "An introductory chapter on The Republic.",
+    "dc:list": [1, 2, 3, 4, 5],
+    "dc:title": "The Introduction"
+  }]
+}
\ No newline at end of file
--- a/test-suite/tests/frame-manifest.jsonld	Mon May 21 22:34:05 2012 -0400
+++ b/test-suite/tests/frame-manifest.jsonld	Mon May 21 22:34:33 2012 -0400
@@ -118,5 +118,17 @@
       "input": "frame-0019-in.jsonld",
       "frame": "frame-0019-frame.jsonld",
       "expect": "frame-0019-out.jsonld"
+   }, {
+      "@type": ["test:TestCase", "jld:FrameTest"],
+      "name": "Blank nodes in an array",
+      "input": "frame-0020-in.jsonld",
+      "frame": "frame-0020-frame.jsonld",
+      "expect": "frame-0020-out.jsonld"
+   }, {
+      "@type": ["test:TestCase", "jld:FrameTest"],
+      "name": "Blank nodes in @type",
+      "input": "frame-0021-in.jsonld",
+      "frame": "frame-0021-frame.jsonld",
+      "expect": "frame-0021-out.jsonld"
    }]
 }