1package gojsonld
2
3// /**
4//  * A container object to maintain state relating to JsonLdOptions and the
5//  * current Context, and push these into the relevant algorithms in
6//  * JsonLdProcessor as necessary.
7//  *
8//  * @author tristan
9//  */
10// public class JsonLdApi {
11
12//     private final Logger log = LoggerFactory.getLogger(this.getClass());
13
14//     JsonLdOptions opts;
15//     Object value = null;
16//     Context context = null;
17
18//     /**
19//      * Constructs an empty JsonLdApi object using the default JsonLdOptions, and
20//      * without initialization.
21//      */
22//     public JsonLdApi() {
23//         this(new JsonLdOptions(""));
24//     }
25
26//     /**
27//      * Constructs a JsonLdApi object using the given object as the initial
28//      * JSON-LD object, and the given JsonLdOptions.
29//      *
30//      * @param input
31//      *            The initial JSON-LD object.
32//      * @param opts
33//      *            The JsonLdOptions to use.
34//      * @throws JsonLdError
35//      *             If there is an error initializing using the object and
36//      *             options.
37//      */
38//     public JsonLdApi(Object input, JsonLdOptions opts) throws JsonLdError {
39//         this(opts);
40//         initialize(input, null);
41//     }
42
43//     /**
44//      * Constructs a JsonLdApi object using the given object as the initial
45//      * JSON-LD object, the given context, and the given JsonLdOptions.
46//      *
47//      * @param input
48//      *            The initial JSON-LD object.
49//      * @param context
50//      *            The initial context.
51//      * @param opts
52//      *            The JsonLdOptions to use.
53//      * @throws JsonLdError
54//      *             If there is an error initializing using the object and
55//      *             options.
56//      */
57//     public JsonLdApi(Object input, Object context, JsonLdOptions opts) throws JsonLdError {
58//         this(opts);
59//         initialize(input, null);
60//     }
61
62//     /**
63//      * Constructs an empty JsonLdApi object using the given JsonLdOptions, and
64//      * without initialization. <br>
65//      * If the JsonLdOptions parameter is null, then the default options are
66//      * used.
67//      *
68//      * @param opts
69//      *            The JsonLdOptions to use.
70//      */
71//     public JsonLdApi(JsonLdOptions opts) {
72//         if (opts == null) {
73//             opts = new JsonLdOptions("");
74//         } else {
75//             this.opts = opts;
76//         }
77//     }
78
79//     /**
80//      * Initializes this object by cloning the input object using
81//      * {@link JsonLdUtils#clone(Object)}, and by parsing the context using
82//      * {@link Context#parse(Object)}.
83//      *
84//      * @param input
85//      *            The initial object, which is to be cloned and used in
86//      *            operations.
87//      * @param context
88//      *            The context object, which is to be parsed and used in
89//      *            operations.
90//      * @throws JsonLdError
91//      *             If there was an error cloning the object, or in parsing the
92//      *             context.
93//      */
94//     private void initialize(Object input, Object context) throws JsonLdError {
95//         if (input instanceof List || input instanceof Map) {
96//             this.value = JsonLdUtils.clone(input);
97//         }
98//         // TODO: string/IO input
99//         this.context = new Context(opts);
100//         if (context != null) {
101//             this.context = this.context.parse(context);
102//         }
103//     }
104
105//     /***
106//      * ____ _ _ _ _ _ _ / ___|___ _ __ ___ _ __ __ _ ___| |_ / \ | | __ _ ___ _
107//      * __(_) |_| |__ _ __ ___ | | / _ \| '_ ` _ \| '_ \ / _` |/ __| __| / _ \ |
108//      * |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \ | |__| (_) | | | | | | |_) | (_|
109//      * | (__| |_ / ___ \| | (_| | (_) | | | | |_| | | | | | | | | \____\___/|_|
110//      * |_| |_| .__/ \__,_|\___|\__| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_|
111//      * |_| |_| |___/
112//      */
113
114//     /**
115//      * Compaction Algorithm
116//      *
117//      * http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm
118//      *
119//      * @param activeCtx
120//      *            The Active Context
121//      * @param activeProperty
122//      *            The Active Property
123//      * @param element
124//      *            The current element
125//      * @param compactArrays
126//      *            True to compact arrays.
127//      * @return The compacted JSON-LD object.
128//      * @throws JsonLdError
129//      *             If there was an error during compaction.
130//      */
131//     public Object compact(Context activeCtx, String activeProperty, Object element,
132//             boolean compactArrays) throws JsonLdError {
133//         // 2)
134//         if (element instanceof List) {
135//             // 2.1)
136//             final List<Object> result = new ArrayList<Object>();
137//             // 2.2)
138//             for (final Object item : (List<Object>) element) {
139//                 // 2.2.1)
140//                 final Object compactedItem = compact(activeCtx, activeProperty, item, compactArrays);
141//                 // 2.2.2)
142//                 if (compactedItem != null) {
143//                     result.add(compactedItem);
144//                 }
145//             }
146//             // 2.3)
147//             if (compactArrays && result.size() == 1
148//                     && activeCtx.getContainer(activeProperty) == null) {
149//                 return result.get(0);
150//             }
151//             // 2.4)
152//             return result;
153//         }
154
155//         // 3)
156//         if (element instanceof Map) {
157//             // access helper
158//             final Map<String, Object> elem = (Map<String, Object>) element;
159
160//             // 4
161//             if (elem.containsKey("@value") || elem.containsKey("@id")) {
162//                 final Object compactedValue = activeCtx.compactValue(activeProperty, elem);
163//                 if (!(compactedValue instanceof Map || compactedValue instanceof List)) {
164//                     return compactedValue;
165//                 }
166//             }
167//             // 5)
168//             final boolean insideReverse = ("@reverse".equals(activeProperty));
169
170//             // 6)
171//             final Map<String, Object> result = new LinkedHashMap<String, Object>();
172//             // 7)
173//             final List<String> keys = new ArrayList<String>(elem.keySet());
174//             Collections.sort(keys);
175//             for (final String expandedProperty : keys) {
176//                 final Object expandedValue = elem.get(expandedProperty);
177
178//                 // 7.1)
179//                 if ("@id".equals(expandedProperty) || "@type".equals(expandedProperty)) {
180//                     Object compactedValue;
181
182//                     // 7.1.1)
183//                     if (expandedValue instanceof String) {
184//                         compactedValue = activeCtx.compactIri((String) expandedValue,
185//                                 "@type".equals(expandedProperty));
186//                     }
187//                     // 7.1.2)
188//                     else {
189//                         final List<String> types = new ArrayList<String>();
190//                         // 7.1.2.2)
191//                         for (final String expandedType : (List<String>) expandedValue) {
192//                             types.add(activeCtx.compactIri(expandedType, true));
193//                         }
194//                         // 7.1.2.3)
195//                         if (types.size() == 1) {
196//                             compactedValue = types.get(0);
197//                         } else {
198//                             compactedValue = types;
199//                         }
200//                     }
201
202//                     // 7.1.3)
203//                     final String alias = activeCtx.compactIri(expandedProperty, true);
204//                     // 7.1.4)
205//                     result.put(alias, compactedValue);
206//                     continue;
207//                     // TODO: old add value code, see if it's still relevant?
208//                     // addValue(rval, alias, compactedValue,
209//                     // isArray(compactedValue)
210//                     // && ((List<Object>) expandedValue).size() == 0);
211//                 }
212
213//                 // 7.2)
214//                 if ("@reverse".equals(expandedProperty)) {
215//                     // 7.2.1)
216//                     final Map<String, Object> compactedValue = (Map<String, Object>) compact(
217//                             activeCtx, "@reverse", expandedValue, compactArrays);
218
219//                     // 7.2.2)
220//                     // Note: Must create a new set to avoid modifying the set we
221//                     // are iterating over
222//                     for (final String property : new HashSet<String>(compactedValue.keySet())) {
223//                         final Object value = compactedValue.get(property);
224//                         // 7.2.2.1)
225//                         if (activeCtx.isReverseProperty(property)) {
226//                             // 7.2.2.1.1)
227//                             if (("@set".equals(activeCtx.getContainer(property)) || !compactArrays)
228//                                     && !(value instanceof List)) {
229//                                 final List<Object> tmp = new ArrayList<Object>();
230//                                 tmp.add(value);
231//                                 result.put(property, tmp);
232//                             }
233//                             // 7.2.2.1.2)
234//                             if (!result.containsKey(property)) {
235//                                 result.put(property, value);
236//                             }
237//                             // 7.2.2.1.3)
238//                             else {
239//                                 if (!(result.get(property) instanceof List)) {
240//                                     final List<Object> tmp = new ArrayList<Object>();
241//                                     tmp.add(result.put(property, tmp));
242//                                 }
243//                                 if (value instanceof List) {
244//                                     ((List<Object>) result.get(property))
245//                                             .addAll((List<Object>) value);
246//                                 } else {
247//                                     ((List<Object>) result.get(property)).add(value);
248//                                 }
249//                             }
250//                             // 7.2.2.1.4)
251//                             compactedValue.remove(property);
252//                         }
253//                     }
254//                     // 7.2.3)
255//                     if (!compactedValue.isEmpty()) {
256//                         // 7.2.3.1)
257//                         final String alias = activeCtx.compactIri("@reverse", true);
258//                         // 7.2.3.2)
259//                         result.put(alias, compactedValue);
260//                     }
261//                     // 7.2.4)
262//                     continue;
263//                 }
264
265//                 // 7.3)
266//                 if ("@index".equals(expandedProperty)
267//                         && "@index".equals(activeCtx.getContainer(activeProperty))) {
268//                     continue;
269//                 }
270//                 // 7.4)
271//                 else if ("@index".equals(expandedProperty) || "@value".equals(expandedProperty)
272//                         || "@language".equals(expandedProperty)) {
273//                     // 7.4.1)
274//                     final String alias = activeCtx.compactIri(expandedProperty, true);
275//                     // 7.4.2)
276//                     result.put(alias, expandedValue);
277//                     continue;
278//                 }
279
280//                 // NOTE: expanded value must be an array due to expansion
281//                 // algorithm.
282
283//                 // 7.5)
284//                 if (((List<Object>) expandedValue).size() == 0) {
285//                     // 7.5.1)
286//                     final String itemActiveProperty = activeCtx.compactIri(expandedProperty,
287//                             expandedValue, true, insideReverse);
288//                     // 7.5.2)
289//                     if (!result.containsKey(itemActiveProperty)) {
290//                         result.put(itemActiveProperty, new ArrayList<Object>());
291//                     } else {
292//                         final Object value = result.get(itemActiveProperty);
293//                         if (!(value instanceof List)) {
294//                             final List<Object> tmp = new ArrayList<Object>();
295//                             tmp.add(value);
296//                             result.put(itemActiveProperty, tmp);
297//                         }
298//                     }
299//                 }
300
301//                 // 7.6)
302//                 for (final Object expandedItem : (List<Object>) expandedValue) {
303//                     // 7.6.1)
304//                     final String itemActiveProperty = activeCtx.compactIri(expandedProperty,
305//                             expandedItem, true, insideReverse);
306//                     // 7.6.2)
307//                     final String container = activeCtx.getContainer(itemActiveProperty);
308
309//                     // get @list value if appropriate
310//                     final boolean isList = (expandedItem instanceof Map && ((Map<String, Object>) expandedItem)
311//                             .containsKey("@list"));
312//                     Object list = null;
313//                     if (isList) {
314//                         list = ((Map<String, Object>) expandedItem).get("@list");
315//                     }
316
317//                     // 7.6.3)
318//                     Object compactedItem = compact(activeCtx, itemActiveProperty, isList ? list
319//                             : expandedItem, compactArrays);
320
321//                     // 7.6.4)
322//                     if (isList) {
323//                         // 7.6.4.1)
324//                         if (!(compactedItem instanceof List)) {
325//                             final List<Object> tmp = new ArrayList<Object>();
326//                             tmp.add(compactedItem);
327//                             compactedItem = tmp;
328//                         }
329//                         // 7.6.4.2)
330//                         if (!"@list".equals(container)) {
331//                             // 7.6.4.2.1)
332//                             final Map<String, Object> wrapper = new LinkedHashMap<String, Object>();
333//                             // TODO: SPEC: no mention of vocab = true
334//                             wrapper.put(activeCtx.compactIri("@list", true), compactedItem);
335//                             compactedItem = wrapper;
336
337//                             // 7.6.4.2.2)
338//                             if (((Map<String, Object>) expandedItem).containsKey("@index")) {
339//                                 ((Map<String, Object>) compactedItem).put(
340//                                         // TODO: SPEC: no mention of vocab =
341//                                         // true
342//                                         activeCtx.compactIri("@index", true),
343//                                         ((Map<String, Object>) expandedItem).get("@index"));
344//                             }
345//                         }
346//                         // 7.6.4.3)
347//                         else if (result.containsKey(itemActiveProperty)) {
348//                             throw new JsonLdError(Error.COMPACTION_TO_LIST_OF_LISTS,
349//                                     "There cannot be two list objects associated with an active property that has a container mapping");
350//                         }
351//                     }
352
353//                     // 7.6.5)
354//                     if ("@language".equals(container) || "@index".equals(container)) {
355//                         // 7.6.5.1)
356//                         Map<String, Object> mapObject;
357//                         if (result.containsKey(itemActiveProperty)) {
358//                             mapObject = (Map<String, Object>) result.get(itemActiveProperty);
359//                         } else {
360//                             mapObject = new LinkedHashMap<String, Object>();
361//                             result.put(itemActiveProperty, mapObject);
362//                         }
363
364//                         // 7.6.5.2)
365//                         if ("@language".equals(container)
366//                                 && (compactedItem instanceof Map && ((Map<String, Object>) compactedItem)
367//                                         .containsKey("@value"))) {
368//                             compactedItem = ((Map<String, Object>) compactedItem).get("@value");
369//                         }
370
371//                         // 7.6.5.3)
372//                         final String mapKey = (String) ((Map<String, Object>) expandedItem)
373//                                 .get(container);
374//                         // 7.6.5.4)
375//                         if (!mapObject.containsKey(mapKey)) {
376//                             mapObject.put(mapKey, compactedItem);
377//                         } else {
378//                             List<Object> tmp;
379//                             if (!(mapObject.get(mapKey) instanceof List)) {
380//                                 tmp = new ArrayList<Object>();
381//                                 tmp.add(mapObject.put(mapKey, tmp));
382//                             } else {
383//                                 tmp = (List<Object>) mapObject.get(mapKey);
384//                             }
385//                             tmp.add(compactedItem);
386//                         }
387//                     }
388//                     // 7.6.6)
389//                     else {
390//                         // 7.6.6.1)
391//                         final Boolean check = (!compactArrays || "@set".equals(container)
392//                                 || "@list".equals(container) || "@list".equals(expandedProperty) || "@graph"
393//                                     .equals(expandedProperty))
394//                                 && (!(compactedItem instanceof List));
395//                         if (check) {
396//                             final List<Object> tmp = new ArrayList<Object>();
397//                             tmp.add(compactedItem);
398//                             compactedItem = tmp;
399//                         }
400//                         // 7.6.6.2)
401//                         if (!result.containsKey(itemActiveProperty)) {
402//                             result.put(itemActiveProperty, compactedItem);
403//                         } else {
404//                             if (!(result.get(itemActiveProperty) instanceof List)) {
405//                                 final List<Object> tmp = new ArrayList<Object>();
406//                                 tmp.add(result.put(itemActiveProperty, tmp));
407//                             }
408//                             if (compactedItem instanceof List) {
409//                                 ((List<Object>) result.get(itemActiveProperty))
410//                                         .addAll((List<Object>) compactedItem);
411//                             } else {
412//                                 ((List<Object>) result.get(itemActiveProperty)).add(compactedItem);
413//                             }
414//                         }
415
416//                     }
417//                 }
418//             }
419//             // 8)
420//             return result;
421//         }
422
423//         // 2)
424//         return element;
425//     }
426
427//     /**
428//      * Compaction Algorithm
429//      *
430//      * http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm
431//      *
432//      * @param activeCtx
433//      *            The Active Context
434//      * @param activeProperty
435//      *            The Active Property
436//      * @param element
437//      *            The current element
438//      * @return The compacted JSON-LD object.
439//      * @throws JsonLdError
440//      *             If there was an error during compaction.
441//      */
442//     public Object compact(Context activeCtx, String activeProperty, Object element)
443//             throws JsonLdError {
444//         return compact(activeCtx, activeProperty, element, true);
445//     }
446
447//     /***
448//      * _____ _ _ _ _ _ _ | ____|_ ___ __ __ _ _ __ __| | / \ | | __ _ ___ _
449//      * __(_) |_| |__ _ __ ___ | _| \ \/ / '_ \ / _` | '_ \ / _` | / _ \ | |/ _`
450//      * |/ _ \| '__| | __| '_ \| '_ ` _ \ | |___ > <| |_) | (_| | | | | (_| | /
451//      * ___ \| | (_| | (_) | | | | |_| | | | | | | | | |_____/_/\_\ .__/ \__,_|_|
452//      * |_|\__,_| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| |_| |___/
453//      */
454
455//     /**
456//      * Expansion Algorithm
457//      *
458//      * http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
459//      *
460//      * @param activeCtx
461//      *            The Active Context
462//      * @param activeProperty
463//      *            The Active Property
464//      * @param element
465//      *            The current element
466//      * @return The expanded JSON-LD object.
467//      * @throws JsonLdError
468//      *             If there was an error during expansion.
469//      */
470//     public Object expand(Context activeCtx, String activeProperty, Object element)
471//             throws JsonLdError {
472//         // 1)
473//         if (element == null) {
474//             return null;
475//         }
476
477//         // 3)
478//         if (element instanceof List) {
479//             // 3.1)
480//             final List<Object> result = new ArrayList<Object>();
481//             // 3.2)
482//             for (final Object item : (List<Object>) element) {
483//                 // 3.2.1)
484//                 final Object v = expand(activeCtx, activeProperty, item);
485//                 // 3.2.2)
486//                 if (("@list".equals(activeProperty) || "@list".equals(activeCtx
487//                         .getContainer(activeProperty)))
488//                         && (v instanceof List || (v instanceof Map && ((Map<String, Object>) v)
489//                                 .containsKey("@list")))) {
490//                     throw new JsonLdError(Error.LIST_OF_LISTS, "lists of lists are not permitted.");
491//                 }
492//                 // 3.2.3)
493//                 else if (v != null) {
494//                     if (v instanceof List) {
495//                         result.addAll((Collection<? extends Object>) v);
496//                     } else {
497//                         result.add(v);
498//                     }
499//                 }
500//             }
501//             // 3.3)
502//             return result;
503//         }
504//         // 4)
505//         else if (element instanceof Map) {
506//             // access helper
507//             final Map<String, Object> elem = (Map<String, Object>) element;
508//             // 5)
509//             if (elem.containsKey("@context")) {
510//                 activeCtx = activeCtx.parse(elem.get("@context"));
511//             }
512//             // 6)
513//             Map<String, Object> result = new LinkedHashMap<String, Object>();
514//             // 7)
515//             final List<String> keys = new ArrayList<String>(elem.keySet());
516//             Collections.sort(keys);
517//             for (final String key : keys) {
518//                 final Object value = elem.get(key);
519//                 // 7.1)
520//                 if (key.equals("@context")) {
521//                     continue;
522//                 }
523//                 // 7.2)
524//                 final String expandedProperty = activeCtx.expandIri(key, false, true, null, null);
525//                 Object expandedValue = null;
526//                 // 7.3)
527//                 if (expandedProperty == null
528//                         || (!expandedProperty.contains(":") && !isKeyword(expandedProperty))) {
529//                     continue;
530//                 }
531//                 // 7.4)
532//                 if (isKeyword(expandedProperty)) {
533//                     // 7.4.1)
534//                     if ("@reverse".equals(activeProperty)) {
535//                         throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_MAP,
536//                                 "a keyword cannot be used as a @reverse propery");
537//                     }
538//                     // 7.4.2)
539//                     if (result.containsKey(expandedProperty)) {
540//                         throw new JsonLdError(Error.COLLIDING_KEYWORDS, expandedProperty
541//                                 + " already exists in result");
542//                     }
543//                     // 7.4.3)
544//                     if ("@id".equals(expandedProperty)) {
545//                         if (!(value instanceof String)) {
546//                             throw new JsonLdError(Error.INVALID_ID_VALUE,
547//                                     "value of @id must be a string");
548//                         }
549//                         expandedValue = activeCtx
550//                                 .expandIri((String) value, true, false, null, null);
551//                     }
552//                     // 7.4.4)
553//                     else if ("@type".equals(expandedProperty)) {
554//                         if (value instanceof List) {
555//                             expandedValue = new ArrayList<String>();
556//                             for (final Object v : (List) value) {
557//                                 if (!(v instanceof String)) {
558//                                     throw new JsonLdError(Error.INVALID_TYPE_VALUE,
559//                                             "@type value must be a string or array of strings");
560//                                 }
561//                                 ((List<String>) expandedValue).add(activeCtx.expandIri((String) v,
562//                                         true, true, null, null));
563//                             }
564//                         } else if (value instanceof String) {
565//                             expandedValue = activeCtx.expandIri((String) value, true, true, null,
566//                                     null);
567//                         }
568//                         // TODO: SPEC: no mention of empty map check
569//                         else if (value instanceof Map) {
570//                             if (((Map<String, Object>) value).size() != 0) {
571//                                 throw new JsonLdError(Error.INVALID_TYPE_VALUE,
572//                                         "@type value must be a an empty object for framing");
573//                             }
574//                             expandedValue = value;
575//                         } else {
576//                             throw new JsonLdError(Error.INVALID_TYPE_VALUE,
577//                                     "@type value must be a string or array of strings");
578//                         }
579//                     }
580//                     // 7.4.5)
581//                     else if ("@graph".equals(expandedProperty)) {
582//                         expandedValue = expand(activeCtx, "@graph", value);
583//                     }
584//                     // 7.4.6)
585//                     else if ("@value".equals(expandedProperty)) {
586//                         if (value != null && (value instanceof Map || value instanceof List)) {
587//                             throw new JsonLdError(Error.INVALID_VALUE_OBJECT_VALUE, "value of "
588//                                     + expandedProperty + " must be a scalar or null");
589//                         }
590//                         expandedValue = value;
591//                         if (expandedValue == null) {
592//                             result.put("@value", null);
593//                             continue;
594//                         }
595//                     }
596//                     // 7.4.7)
597//                     else if ("@language".equals(expandedProperty)) {
598//                         if (!(value instanceof String)) {
599//                             throw new JsonLdError(Error.INVALID_LANGUAGE_TAGGED_STRING, "Value of "
600//                                     + expandedProperty + " must be a string");
601//                         }
602//                         expandedValue = ((String) value).toLowerCase();
603//                     }
604//                     // 7.4.8)
605//                     else if ("@index".equals(expandedProperty)) {
606//                         if (!(value instanceof String)) {
607//                             throw new JsonLdError(Error.INVALID_INDEX_VALUE, "Value of "
608//                                     + expandedProperty + " must be a string");
609//                         }
610//                         expandedValue = value;
611//                     }
612//                     // 7.4.9)
613//                     else if ("@list".equals(expandedProperty)) {
614//                         // 7.4.9.1)
615//                         if (activeProperty == null || "@graph".equals(activeProperty)) {
616//                             continue;
617//                         }
618//                         // 7.4.9.2)
619//                         expandedValue = expand(activeCtx, activeProperty, value);
620
621//                         // NOTE: step not in the spec yet
622//                         if (!(expandedValue instanceof List)) {
623//                             final List<Object> tmp = new ArrayList<Object>();
624//                             tmp.add(expandedValue);
625//                             expandedValue = tmp;
626//                         }
627
628//                         // 7.4.9.3)
629//                         for (final Object o : (List<Object>) expandedValue) {
630//                             if (o instanceof Map && ((Map<String, Object>) o).containsKey("@list")) {
631//                                 throw new JsonLdError(Error.LIST_OF_LISTS,
632//                                         "A list may not contain another list");
633//                             }
634//                         }
635//                     }
636//                     // 7.4.10)
637//                     else if ("@set".equals(expandedProperty)) {
638//                         expandedValue = expand(activeCtx, activeProperty, value);
639//                     }
640//                     // 7.4.11)
641//                     else if ("@reverse".equals(expandedProperty)) {
642//                         if (!(value instanceof Map)) {
643//                             throw new JsonLdError(Error.INVALID_REVERSE_VALUE,
644//                                     "@reverse value must be an object");
645//                         }
646//                         // 7.4.11.1)
647//                         expandedValue = expand(activeCtx, "@reverse", value);
648//                         // NOTE: algorithm assumes the result is a map
649//                         // 7.4.11.2)
650//                         if (((Map<String, Object>) expandedValue).containsKey("@reverse")) {
651//                             final Map<String, Object> reverse = (Map<String, Object>) ((Map<String, Object>) expandedValue)
652//                                     .get("@reverse");
653//                             for (final String property : reverse.keySet()) {
654//                                 final Object item = reverse.get(property);
655//                                 // 7.4.11.2.1)
656//                                 if (!result.containsKey(property)) {
657//                                     result.put(property, new ArrayList<Object>());
658//                                 }
659//                                 // 7.4.11.2.2)
660//                                 if (item instanceof List) {
661//                                     ((List<Object>) result.get(property))
662//                                             .addAll((List<Object>) item);
663//                                 } else {
664//                                     ((List<Object>) result.get(property)).add(item);
665//                                 }
666//                             }
667//                         }
668//                         // 7.4.11.3)
669//                         if (((Map<String, Object>) expandedValue).size() > (((Map<String, Object>) expandedValue)
670//                                 .containsKey("@reverse") ? 1 : 0)) {
671//                             // 7.4.11.3.1)
672//                             if (!result.containsKey("@reverse")) {
673//                                 result.put("@reverse", new LinkedHashMap<String, Object>());
674//                             }
675//                             // 7.4.11.3.2)
676//                             final Map<String, Object> reverseMap = (Map<String, Object>) result
677//                                     .get("@reverse");
678//                             // 7.4.11.3.3)
679//                             for (final String property : ((Map<String, Object>) expandedValue)
680//                                     .keySet()) {
681//                                 if ("@reverse".equals(property)) {
682//                                     continue;
683//                                 }
684//                                 // 7.4.11.3.3.1)
685//                                 final List<Object> items = (List<Object>) ((Map<String, Object>) expandedValue)
686//                                         .get(property);
687//                                 for (final Object item : items) {
688//                                     // 7.4.11.3.3.1.1)
689//                                     if (item instanceof Map
690//                                             && (((Map<String, Object>) item).containsKey("@value") || ((Map<String, Object>) item)
691//                                                     .containsKey("@list"))) {
692//                                         throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_VALUE);
693//                                     }
694//                                     // 7.4.11.3.3.1.2)
695//                                     if (!reverseMap.containsKey(property)) {
696//                                         reverseMap.put(property, new ArrayList<Object>());
697//                                     }
698//                                     // 7.4.11.3.3.1.3)
699//                                     ((List<Object>) reverseMap.get(property)).add(item);
700//                                 }
701//                             }
702//                         }
703//                         // 7.4.11.4)
704//                         continue;
705//                     }
706//                     // TODO: SPEC no mention of @explicit etc in spec
707//                     else if ("@explicit".equals(expandedProperty)
708//                             || "@default".equals(expandedProperty)
709//                             || "@embed".equals(expandedProperty)
710//                             || "@embedChildren".equals(expandedProperty)
711//                             || "@omitDefault".equals(expandedProperty)) {
712//                         expandedValue = expand(activeCtx, expandedProperty, value);
713//                     }
714//                     // 7.4.12)
715//                     if (expandedValue != null) {
716//                         result.put(expandedProperty, expandedValue);
717//                     }
718//                     // 7.4.13)
719//                     continue;
720//                 }
721//                 // 7.5
722//                 else if ("@language".equals(activeCtx.getContainer(key)) && value instanceof Map) {
723//                     // 7.5.1)
724//                     expandedValue = new ArrayList<Object>();
725//                     // 7.5.2)
726//                     for (final String language : ((Map<String, Object>) value).keySet()) {
727//                         Object languageValue = ((Map<String, Object>) value).get(language);
728//                         // 7.5.2.1)
729//                         if (!(languageValue instanceof List)) {
730//                             final Object tmp = languageValue;
731//                             languageValue = new ArrayList<Object>();
732//                             ((List<Object>) languageValue).add(tmp);
733//                         }
734//                         // 7.5.2.2)
735//                         for (final Object item : (List<Object>) languageValue) {
736//                             // 7.5.2.2.1)
737//                             if (!(item instanceof String)) {
738//                                 throw new JsonLdError(Error.INVALID_LANGUAGE_MAP_VALUE, "Expected "
739//                                         + item.toString() + " to be a string");
740//                             }
741//                             // 7.5.2.2.2)
742//                             final Map<String, Object> tmp = new LinkedHashMap<String, Object>();
743//                             tmp.put("@value", item);
744//                             tmp.put("@language", language.toLowerCase());
745//                             ((List<Object>) expandedValue).add(tmp);
746//                         }
747//                     }
748//                 }
749//                 // 7.6)
750//                 else if ("@index".equals(activeCtx.getContainer(key)) && value instanceof Map) {
751//                     // 7.6.1)
752//                     expandedValue = new ArrayList<Object>();
753//                     // 7.6.2)
754//                     final List<String> indexKeys = new ArrayList<String>(
755//                             ((Map<String, Object>) value).keySet());
756//                     Collections.sort(indexKeys);
757//                     for (final String index : indexKeys) {
758//                         Object indexValue = ((Map<String, Object>) value).get(index);
759//                         // 7.6.2.1)
760//                         if (!(indexValue instanceof List)) {
761//                             final Object tmp = indexValue;
762//                             indexValue = new ArrayList<Object>();
763//                             ((List<Object>) indexValue).add(tmp);
764//                         }
765//                         // 7.6.2.2)
766//                         indexValue = expand(activeCtx, key, indexValue);
767//                         // 7.6.2.3)
768//                         for (final Map<String, Object> item : (List<Map<String, Object>>) indexValue) {
769//                             // 7.6.2.3.1)
770//                             if (!item.containsKey("@index")) {
771//                                 item.put("@index", index);
772//                             }
773//                             // 7.6.2.3.2)
774//                             ((List<Object>) expandedValue).add(item);
775//                         }
776//                     }
777//                 }
778//                 // 7.7)
779//                 else {
780//                     expandedValue = expand(activeCtx, key, value);
781//                 }
782//                 // 7.8)
783//                 if (expandedValue == null) {
784//                     continue;
785//                 }
786//                 // 7.9)
787//                 if ("@list".equals(activeCtx.getContainer(key))) {
788//                     if (!(expandedValue instanceof Map)
789//                             || !((Map<String, Object>) expandedValue).containsKey("@list")) {
790//                         Object tmp = expandedValue;
791//                         if (!(tmp instanceof List)) {
792//                             tmp = new ArrayList<Object>();
793//                             ((List<Object>) tmp).add(expandedValue);
794//                         }
795//                         expandedValue = new LinkedHashMap<String, Object>();
796//                         ((Map<String, Object>) expandedValue).put("@list", tmp);
797//                     }
798//                 }
799//                 // 7.10)
800//                 if (activeCtx.isReverseProperty(key)) {
801//                     // 7.10.1)
802//                     if (!result.containsKey("@reverse")) {
803//                         result.put("@reverse", new LinkedHashMap<String, Object>());
804//                     }
805//                     // 7.10.2)
806//                     final Map<String, Object> reverseMap = (Map<String, Object>) result
807//                             .get("@reverse");
808//                     // 7.10.3)
809//                     if (!(expandedValue instanceof List)) {
810//                         final Object tmp = expandedValue;
811//                         expandedValue = new ArrayList<Object>();
812//                         ((List<Object>) expandedValue).add(tmp);
813//                     }
814//                     // 7.10.4)
815//                     for (final Object item : (List<Object>) expandedValue) {
816//                         // 7.10.4.1)
817//                         if (item instanceof Map
818//                                 && (((Map<String, Object>) item).containsKey("@value") || ((Map<String, Object>) item)
819//                                         .containsKey("@list"))) {
820//                             throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_VALUE);
821//                         }
822//                         // 7.10.4.2)
823//                         if (!reverseMap.containsKey(expandedProperty)) {
824//                             reverseMap.put(expandedProperty, new ArrayList<Object>());
825//                         }
826//                         // 7.10.4.3)
827//                         if (item instanceof List) {
828//                             ((List<Object>) reverseMap.get(expandedProperty))
829//                                     .addAll((List<Object>) item);
830//                         } else {
831//                             ((List<Object>) reverseMap.get(expandedProperty)).add(item);
832//                         }
833//                     }
834//                 }
835//                 // 7.11)
836//                 else {
837//                     // 7.11.1)
838//                     if (!result.containsKey(expandedProperty)) {
839//                         result.put(expandedProperty, new ArrayList<Object>());
840//                     }
841//                     // 7.11.2)
842//                     if (expandedValue instanceof List) {
843//                         ((List<Object>) result.get(expandedProperty))
844//                                 .addAll((List<Object>) expandedValue);
845//                     } else {
846//                         ((List<Object>) result.get(expandedProperty)).add(expandedValue);
847//                     }
848//                 }
849//             }
850//             // 8)
851//             if (result.containsKey("@value")) {
852//                 // 8.1)
853//                 // TODO: is this method faster than just using containsKey for
854//                 // each?
855//                 final Set<String> keySet = new HashSet(result.keySet());
856//                 keySet.remove("@value");
857//                 keySet.remove("@index");
858//                 final boolean langremoved = keySet.remove("@language");
859//                 final boolean typeremoved = keySet.remove("@type");
860//                 if ((langremoved && typeremoved) || !keySet.isEmpty()) {
861//                     throw new JsonLdError(Error.INVALID_VALUE_OBJECT,
862//                             "value object has unknown keys");
863//                 }
864//                 // 8.2)
865//                 final Object rval = result.get("@value");
866//                 if (rval == null) {
867//                     // nothing else is possible with result if we set it to
868//                     // null, so simply return it
869//                     return null;
870//                 }
871//                 // 8.3)
872//                 if (!(rval instanceof String) && result.containsKey("@language")) {
873//                     throw new JsonLdError(Error.INVALID_LANGUAGE_TAGGED_VALUE,
874//                             "when @language is used, @value must be a string");
875//                 }
876//                 // 8.4)
877//                 else if (result.containsKey("@type")) {
878//                     // TODO: is this enough for "is an IRI"
879//                     if (!(result.get("@type") instanceof String)
880//                             || ((String) result.get("@type")).startsWith("_:")
881//                             || !((String) result.get("@type")).contains(":")) {
882//                         throw new JsonLdError(Error.INVALID_TYPED_VALUE,
883//                                 "value of @type must be an IRI");
884//                     }
885//                 }
886//             }
887//             // 9)
888//             else if (result.containsKey("@type")) {
889//                 final Object rtype = result.get("@type");
890//                 if (!(rtype instanceof List)) {
891//                     final List<Object> tmp = new ArrayList<Object>();
892//                     tmp.add(rtype);
893//                     result.put("@type", tmp);
894//                 }
895//             }
896//             // 10)
897//             else if (result.containsKey("@set") || result.containsKey("@list")) {
898//                 // 10.1)
899//                 if (result.size() > (result.containsKey("@index") ? 2 : 1)) {
900//                     throw new JsonLdError(Error.INVALID_SET_OR_LIST_OBJECT,
901//                             "@set or @list may only contain @index");
902//                 }
903//                 // 10.2)
904//                 if (result.containsKey("@set")) {
905//                     // result becomes an array here, thus the remaining checks
906//                     // will never be true from here on
907//                     // so simply return the value rather than have to make
908//                     // result an object and cast it with every
909//                     // other use in the function.
910//                     return result.get("@set");
911//                 }
912//             }
913//             // 11)
914//             if (result.containsKey("@language") && result.size() == 1) {
915//                 result = null;
916//             }
917//             // 12)
918//             if (activeProperty == null || "@graph".equals(activeProperty)) {
919//                 // 12.1)
920//                 if (result != null
921//                         && (result.size() == 0 || result.containsKey("@value") || result
922//                                 .containsKey("@list"))) {
923//                     result = null;
924//                 }
925//                 // 12.2)
926//                 else if (result != null && result.containsKey("@id") && result.size() == 1) {
927//                     result = null;
928//                 }
929//             }
930//             // 13)
931//             return result;
932//         }
933//         // 2) If element is a scalar
934//         else {
935//             // 2.1)
936//             if (activeProperty == null || "@graph".equals(activeProperty)) {
937//                 return null;
938//             }
939//             return activeCtx.expandValue(activeProperty, element);
940//         }
941//     }
942
943//     *
944//      * Expansion Algorithm
945//      *
946//      * http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
947//      *
948//      * @param activeCtx
949//      *            The Active Context
950//      * @param element
951//      *            The current element
952//      * @return The expanded JSON-LD object.
953//      * @throws JsonLdError
954//      *             If there was an error during expansion.
955
956//     public Object expand(Context activeCtx, Object element) throws JsonLdError {
957//         return expand(activeCtx, null, element);
958//     }
959
960//     /***
961//      * _____ _ _ _ _ _ _ _ _ | ___| | __ _| |_| |_ ___ _ __ / \ | | __ _ ___ _
962//      * __(_) |_| |__ _ __ ___ | |_ | |/ _` | __| __/ _ \ '_ \ / _ \ | |/ _` |/ _
963//      * \| '__| | __| '_ \| '_ ` _ \ | _| | | (_| | |_| || __/ | | | / ___ \| |
964//      * (_| | (_) | | | | |_| | | | | | | | | |_| |_|\__,_|\__|\__\___|_| |_| /_/
965//      * \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| |___/
966//      */
967
968//     void generateNodeMap(Object element, Map<String, Object> nodeMap) throws JsonLdError {
969//         generateNodeMap(element, nodeMap, "@default", null, null, null);
970//     }
971
972//     void generateNodeMap(Object element, Map<String, Object> nodeMap, String activeGraph)
973//             throws JsonLdError {
974//         generateNodeMap(element, nodeMap, activeGraph, null, null, null);
975//     }
976
977//     void generateNodeMap(Object element, Map<String, Object> nodeMap, String activeGraph,
978//             Object activeSubject, String activeProperty, Map<String, Object> list)
979//             throws JsonLdError {
980//         // 1)
981//         if (element instanceof List) {
982//             // 1.1)
983//             for (final Object item : (List<Object>) element) {
984//                 generateNodeMap(item, nodeMap, activeGraph, activeSubject, activeProperty, list);
985//             }
986//             return;
987//         }
988
989//         // for convenience
990//         final Map<String, Object> elem = (Map<String, Object>) element;
991
992//         // 2)
993//         if (!nodeMap.containsKey(activeGraph)) {
994//             nodeMap.put(activeGraph, new LinkedHashMap<String, Object>());
995//         }
996//         final Map<String, Object> graph = (Map<String, Object>) nodeMap.get(activeGraph);
997//         Map<String, Object> node = (Map<String, Object>) (activeSubject == null ? null : graph
998//                 .get(activeSubject));
999
1000//         // 3)
1001//         if (elem.containsKey("@type")) {
1002//             // 3.1)
1003//             List<String> oldTypes;
1004//             final List<String> newTypes = new ArrayList<String>();
1005//             if (elem.get("@type") instanceof List) {
1006//                 oldTypes = (List<String>) elem.get("@type");
1007//             } else {
1008//                 oldTypes = new ArrayList<String>();
1009//                 oldTypes.add((String) elem.get("@type"));
1010//             }
1011//             for (final String item : oldTypes) {
1012//                 if (item.startsWith("_:")) {
1013//                     newTypes.add(generateBlankNodeIdentifier(item));
1014//                 } else {
1015//                     newTypes.add(item);
1016//                 }
1017//             }
1018//             if (elem.get("@type") instanceof List) {
1019//                 elem.put("@type", newTypes);
1020//             } else {
1021//                 elem.put("@type", newTypes.get(0));
1022//             }
1023//         }
1024
1025//         // 4)
1026//         if (elem.containsKey("@value")) {
1027//             // 4.1)
1028//             if (list == null) {
1029//                 JsonLdUtils.mergeValue(node, activeProperty, elem);
1030//             }
1031//             // 4.2)
1032//             else {
1033//                 JsonLdUtils.mergeValue(list, "@list", elem);
1034//             }
1035//         }
1036
1037//         // 5)
1038//         else if (elem.containsKey("@list")) {
1039//             // 5.1)
1040//             final Map<String, Object> result = new LinkedHashMap<String, Object>();
1041//             result.put("@list", new ArrayList<Object>());
1042//             // 5.2)
1043//             // for (final Object item : (List<Object>) elem.get("@list")) {
1044//             // generateNodeMap(item, nodeMap, activeGraph, activeSubject,
1045//             // activeProperty, result);
1046//             // }
1047//             generateNodeMap(elem.get("@list"), nodeMap, activeGraph, activeSubject, activeProperty,
1048//                     result);
1049//             // 5.3)
1050//             JsonLdUtils.mergeValue(node, activeProperty, result);
1051//         }
1052
1053//         // 6)
1054//         else {
1055//             // 6.1)
1056//             String id = (String) elem.remove("@id");
1057//             if (id != null) {
1058//                 if (id.startsWith("_:")) {
1059//                     id = generateBlankNodeIdentifier(id);
1060//                 }
1061//             }
1062//             // 6.2)
1063//             else {
1064//                 id = generateBlankNodeIdentifier(null);
1065//             }
1066//             // 6.3)
1067//             if (!graph.containsKey(id)) {
1068//                 final Map<String, Object> tmp = new LinkedHashMap<String, Object>();
1069//                 tmp.put("@id", id);
1070//                 graph.put(id, tmp);
1071//             }
1072//             // 6.4) TODO: SPEC this line is asked for by the spec, but it breaks
1073//             // various tests
1074//             // node = (Map<String, Object>) graph.get(id);
1075//             // 6.5)
1076//             if (activeSubject instanceof Map) {
1077//                 // 6.5.1)
1078//                 JsonLdUtils.mergeValue((Map<String, Object>) graph.get(id), activeProperty,
1079//                         activeSubject);
1080//             }
1081//             // 6.6)
1082//             else if (activeProperty != null) {
1083//                 final Map<String, Object> reference = new LinkedHashMap<String, Object>();
1084//                 reference.put("@id", id);
1085//                 // 6.6.2)
1086//                 if (list == null) {
1087//                     // 6.6.2.1+2)
1088//                     JsonLdUtils.mergeValue(node, activeProperty, reference);
1089//                 }
1090//                 // 6.6.3) TODO: SPEC says to add ELEMENT to @list member, should
1091//                 // be REFERENCE
1092//                 else {
1093//                     JsonLdUtils.mergeValue(list, "@list", reference);
1094//                 }
1095//             }
1096//             // TODO: SPEC this is removed in the spec now, but it's still needed
1097//             // (see 6.4)
1098//             node = (Map<String, Object>) graph.get(id);
1099//             // 6.7)
1100//             if (elem.containsKey("@type")) {
1101//                 for (final Object type : (List<Object>) elem.remove("@type")) {
1102//                     JsonLdUtils.mergeValue(node, "@type", type);
1103//                 }
1104//             }
1105//             // 6.8)
1106//             if (elem.containsKey("@index")) {
1107//                 final Object elemIndex = elem.remove("@index");
1108//                 if (node.containsKey("@index")) {
1109//                     if (!JsonLdUtils.deepCompare(node.get("@index"), elemIndex)) {
1110//                         throw new JsonLdError(Error.CONFLICTING_INDEXES);
1111//                     }
1112//                 } else {
1113//                     node.put("@index", elemIndex);
1114//                 }
1115//             }
1116//             // 6.9)
1117//             if (elem.containsKey("@reverse")) {
1118//                 // 6.9.1)
1119//                 final Map<String, Object> referencedNode = new LinkedHashMap<String, Object>();
1120//                 referencedNode.put("@id", id);
1121//                 // 6.9.2+6.9.4)
1122//                 final Map<String, Object> reverseMap = (Map<String, Object>) elem
1123//                         .remove("@reverse");
1124//                 // 6.9.3)
1125//                 for (final String property : reverseMap.keySet()) {
1126//                     final List<Object> values = (List<Object>) reverseMap.get(property);
1127//                     // 6.9.3.1)
1128//                     for (final Object value : values) {
1129//                         // 6.9.3.1.1)
1130//                         generateNodeMap(value, nodeMap, activeGraph, referencedNode, property, null);
1131//                     }
1132//                 }
1133//             }
1134//             // 6.10)
1135//             if (elem.containsKey("@graph")) {
1136//                 generateNodeMap(elem.remove("@graph"), nodeMap, id, null, null, null);
1137//             }
1138//             // 6.11)
1139//             final List<String> keys = new ArrayList<String>(elem.keySet());
1140//             Collections.sort(keys);
1141//             for (String property : keys) {
1142//                 final Object value = elem.get(property);
1143//                 // 6.11.1)
1144//                 if (property.startsWith("_:")) {
1145//                     property = generateBlankNodeIdentifier(property);
1146//                 }
1147//                 // 6.11.2)
1148//                 if (!node.containsKey(property)) {
1149//                     node.put(property, new ArrayList<Object>());
1150//                 }
1151//                 // 6.11.3)
1152//                 generateNodeMap(value, nodeMap, activeGraph, id, property, null);
1153//             }
1154//         }
1155//     }
1156
1157//     /**
1158//      * Blank Node identifier map specified in:
1159//      *
1160//      * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier
1161//      */
1162//     private final Map<String, String> blankNodeIdentifierMap = new LinkedHashMap<String, String>();
1163
1164//     /**
1165//      * Counter specified in:
1166//      *
1167//      * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier
1168//      */
1169//     private int blankNodeCounter = 0;
1170
1171//     /**
1172//      * Generates a blank node identifier for the given key using the algorithm
1173//      * specified in:
1174//      *
1175//      * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier
1176//      *
1177//      * @param id
1178//      *            The id, or null to generate a fresh, unused, blank node
1179//      *            identifier.
1180//      * @return A blank node identifier based on id if it was not null, or a
1181//      *         fresh, unused, blank node identifier if it was null.
1182//      */
1183//     String generateBlankNodeIdentifier(String id) {
1184//         if (id != null && blankNodeIdentifierMap.containsKey(id)) {
1185//             return blankNodeIdentifierMap.get(id);
1186//         }
1187//         final String bnid = "_:b" + blankNodeCounter++;
1188//         if (id != null) {
1189//             blankNodeIdentifierMap.put(id, bnid);
1190//         }
1191//         return bnid;
1192//     }
1193
1194//     /**
1195//      * Generates a fresh, unused, blank node identifier using the algorithm
1196//      * specified in:
1197//      *
1198//      * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier
1199//      *
1200//      * @return A fresh, unused, blank node identifier.
1201//      */
1202//     String generateBlankNodeIdentifier() {
1203//         return generateBlankNodeIdentifier(null);
1204//     }
1205
1206//     /***
1207//      * _____ _ _ _ _ _ _ | ___| __ __ _ _ __ ___ (_)_ __ __ _ / \ | | __ _ ___ _
1208//      * __(_) |_| |__ _ __ ___ | |_ | '__/ _` | '_ ` _ \| | '_ \ / _` | / _ \ |
1209//      * |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \ | _|| | | (_| | | | | | | | | | |
1210//      * (_| | / ___ \| | (_| | (_) | | | | |_| | | | | | | | | |_| |_| \__,_|_|
1211//      * |_| |_|_|_| |_|\__, | /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
1212//      * |___/ |___/
1213//      */
1214
1215//     private class FramingContext {
1216//         public boolean embed;
1217//         public boolean explicit;
1218//         public boolean omitDefault;
1219
1220//         public FramingContext() {
1221//             embed = true;
1222//             explicit = false;
1223//             omitDefault = false;
1224//             embeds = null;
1225//         }
1226
1227//         public FramingContext(JsonLdOptions opts) {
1228//             this();
1229//             if (opts.getEmbed() != null) {
1230//                 this.embed = opts.getEmbed();
1231//             }
1232//             if (opts.getExplicit() != null) {
1233//                 this.explicit = opts.getExplicit();
1234//             }
1235//             if (opts.getOmitDefault() != null) {
1236//                 this.omitDefault = opts.getOmitDefault();
1237//             }
1238//         }
1239
1240//         public Map<String, EmbedNode> embeds = null;
1241//     }
1242
1243//     private class EmbedNode {
1244//         public Object parent = null;
1245//         public String property = null;
1246//     }
1247
1248//     private Map<String, Object> nodeMap;
1249
1250//     /**
1251//      * Performs JSON-LD <a
1252//      * href="http://json-ld.org/spec/latest/json-ld-framing/">framing</a>.
1253//      *
1254//      * @param input
1255//      *            the expanded JSON-LD to frame.
1256//      * @param frame
1257//      *            the expanded JSON-LD frame to use.
1258//      * @return the framed output.
1259//      * @throws JsonLdError
1260//      *             If the framing was not successful.
1261//      */
1262//     public List<Object> frame(Object input, List<Object> frame) throws JsonLdError {
1263//         // create framing state
1264//         final FramingContext state = new FramingContext(this.opts);
1265
1266//         // use tree map so keys are sotred by default
1267//         final Map<String, Object> nodes = new TreeMap<String, Object>();
1268//         generateNodeMap(input, nodes);
1269//         this.nodeMap = (Map<String, Object>) nodes.get("@default");
1270
1271//         final List<Object> framed = new ArrayList<Object>();
1272//         // NOTE: frame validation is done by the function not allowing anything
1273//         // other than list to me passed
1274//         frame(state, this.nodeMap,
1275//                 (frame != null && frame.size() > 0 ? (Map<String, Object>) frame.get(0)
1276//                         : new LinkedHashMap<String, Object>()), framed, null);
1277
1278//         return framed;
1279//     }
1280
1281//     /**
1282//      * Frames subjects according to the given frame.
1283//      *
1284//      * @param state
1285//      *            the current framing state.
1286//      * @param subjects
1287//      *            the subjects to filter.
1288//      * @param frame
1289//      *            the frame.
1290//      * @param parent
1291//      *            the parent subject or top-level array.
1292//      * @param property
1293//      *            the parent property, initialized to null.
1294//      * @throws JsonLdError
1295//      *             If there was an error during framing.
1296//      */
1297//     private void frame(FramingContext state, Map<String, Object> nodes, Map<String, Object> frame,
1298//             Object parent, String property) throws JsonLdError {
1299
1300//         // filter out subjects that match the frame
1301//         final Map<String, Object> matches = filterNodes(state, nodes, frame);
1302
1303//         // get flags for current frame
1304//         Boolean embedOn = getFrameFlag(frame, "@embed", state.embed);
1305//         final Boolean explicicOn = getFrameFlag(frame, "@explicit", state.explicit);
1306
1307//         // add matches to output
1308//         final List<String> ids = new ArrayList<String>(matches.keySet());
1309//         Collections.sort(ids);
1310//         for (final String id : ids) {
1311//             if (property == null) {
1312//                 state.embeds = new LinkedHashMap<String, EmbedNode>();
1313//             }
1314
1315//             // start output
1316//             final Map<String, Object> output = new LinkedHashMap<String, Object>();
1317//             output.put("@id", id);
1318
1319//             // prepare embed meta info
1320//             final EmbedNode embeddedNode = new EmbedNode();
1321//             embeddedNode.parent = parent;
1322//             embeddedNode.property = property;
1323
1324//             // if embed is on and there is an existing embed
1325//             if (embedOn && state.embeds.containsKey(id)) {
1326//                 final EmbedNode existing = state.embeds.get(id);
1327//                 embedOn = false;
1328
1329//                 if (existing.parent instanceof List) {
1330//                     for (final Object p : (List<Object>) existing.parent) {
1331//                         if (JsonLdUtils.compareValues(output, p)) {
1332//                             embedOn = true;
1333//                             break;
1334//                         }
1335//                     }
1336//                 }
1337//                 // existing embed's parent is an object
1338//                 else {
1339//                     if (((Map<String, Object>) existing.parent).containsKey(existing.property)) {
1340//                         for (final Object v : (List<Object>) ((Map<String, Object>) existing.parent)
1341//                                 .get(existing.property)) {
1342//                             if (v instanceof Map
1343//                                     && Obj.equals(id, ((Map<String, Object>) v).get("@id"))) {
1344//                                 embedOn = true;
1345//                                 break;
1346//                             }
1347//                         }
1348//                     }
1349//                 }
1350
1351//                 // existing embed has already been added, so allow an overwrite
1352//                 if (embedOn) {
1353//                     removeEmbed(state, id);
1354//                 }
1355//             }
1356
1357//             // not embedding, add output without any other properties
1358//             if (!embedOn) {
1359//                 addFrameOutput(state, parent, property, output);
1360//             } else {
1361//                 // add embed meta info
1362//                 state.embeds.put(id, embeddedNode);
1363
1364//                 // iterate over subject properties
1365//                 final Map<String, Object> element = (Map<String, Object>) matches.get(id);
1366//                 List<String> props = new ArrayList<String>(element.keySet());
1367//                 Collections.sort(props);
1368//                 for (final String prop : props) {
1369
1370//                     // copy keywords to output
1371//                     if (isKeyword(prop)) {
1372//                         output.put(prop, JsonLdUtils.clone(element.get(prop)));
1373//                         continue;
1374//                     }
1375
1376//                     // if property isn't in the frame
1377//                     if (!frame.containsKey(prop)) {
1378//                         // if explicit is off, embed values
1379//                         if (!explicicOn) {
1380//                             embedValues(state, element, prop, output);
1381//                         }
1382//                         continue;
1383//                     }
1384
1385//                     // add objects
1386//                     final List<Object> value = (List<Object>) element.get(prop);
1387
1388//                     for (final Object item : value) {
1389
1390//                         // recurse into list
1391//                         if ((item instanceof Map)
1392//                                 && ((Map<String, Object>) item).containsKey("@list")) {
1393//                             // add empty list
1394//                             final Map<String, Object> list = new LinkedHashMap<String, Object>();
1395//                             list.put("@list", new ArrayList<Object>());
1396//                             addFrameOutput(state, output, prop, list);
1397
1398//                             // add list objects
1399//                             for (final Object listitem : (List<Object>) ((Map<String, Object>) item)
1400//                                     .get("@list")) {
1401//                                 // recurse into subject reference
1402//                                 if (JsonLdUtils.isNodeReference(listitem)) {
1403//                                     final Map<String, Object> tmp = new LinkedHashMap<String, Object>();
1404//                                     final String itemid = (String) ((Map<String, Object>) listitem)
1405//                                             .get("@id");
1406//                                     // TODO: nodes may need to be node_map,
1407//                                     // which is global
1408//                                     tmp.put(itemid, this.nodeMap.get(itemid));
1409//                                     frame(state, tmp,
1410//                                             (Map<String, Object>) ((List<Object>) frame.get(prop))
1411//                                                     .get(0), list, "@list");
1412//                                 } else {
1413//                                     // include other values automatcially (TODO:
1414//                                     // may need JsonLdUtils.clone(n))
1415//                                     addFrameOutput(state, list, "@list", listitem);
1416//                                 }
1417//                             }
1418//                         }
1419
1420//                         // recurse into subject reference
1421//                         else if (JsonLdUtils.isNodeReference(item)) {
1422//                             final Map<String, Object> tmp = new LinkedHashMap<String, Object>();
1423//                             final String itemid = (String) ((Map<String, Object>) item).get("@id");
1424//                             // TODO: nodes may need to be node_map, which is
1425//                             // global
1426//                             tmp.put(itemid, this.nodeMap.get(itemid));
1427//                             frame(state, tmp,
1428//                                     (Map<String, Object>) ((List<Object>) frame.get(prop)).get(0),
1429//                                     output, prop);
1430//                         } else {
1431//                             // include other values automatically (TODO: may
1432//                             // need JsonLdUtils.clone(o))
1433//                             addFrameOutput(state, output, prop, item);
1434//                         }
1435//                     }
1436//                 }
1437
1438//                 // handle defaults
1439//                 props = new ArrayList<String>(frame.keySet());
1440//                 Collections.sort(props);
1441//                 for (final String prop : props) {
1442//                     // skip keywords
1443//                     if (isKeyword(prop)) {
1444//                         continue;
1445//                     }
1446
1447//                     final List<Object> pf = (List<Object>) frame.get(prop);
1448//                     Map<String, Object> propertyFrame = pf.size() > 0 ? (Map<String, Object>) pf
1449//                             .get(0) : null;
1450//                     if (propertyFrame == null) {
1451//                         propertyFrame = new LinkedHashMap<String, Object>();
1452//                     }
1453//                     final boolean omitDefaultOn = getFrameFlag(propertyFrame, "@omitDefault",
1454//                             state.omitDefault);
1455//                     if (!omitDefaultOn && !output.containsKey(prop)) {
1456//                         Object def = "@null";
1457//                         if (propertyFrame.containsKey("@default")) {
1458//                             def = JsonLdUtils.clone(propertyFrame.get("@default"));
1459//                         }
1460//                         if (!(def instanceof List)) {
1461//                             final List<Object> tmp = new ArrayList<Object>();
1462//                             tmp.add(def);
1463//                             def = tmp;
1464//                         }
1465//                         final Map<String, Object> tmp1 = new LinkedHashMap<String, Object>();
1466//                         tmp1.put("@preserve", def);
1467//                         final List<Object> tmp2 = new ArrayList<Object>();
1468//                         tmp2.add(tmp1);
1469//                         output.put(prop, tmp2);
1470//                     }
1471//                 }
1472
1473//                 // add output to parent
1474//                 addFrameOutput(state, parent, property, output);
1475//             }
1476//         }
1477//     }
1478
1479//     private Boolean getFrameFlag(Map<String, Object> frame, String name, boolean thedefault) {
1480//         Object value = frame.get(name);
1481//         if (value instanceof List) {
1482//             if (((List<Object>) value).size() > 0) {
1483//                 value = ((List<Object>) value).get(0);
1484//             }
1485//         }
1486//         if (value instanceof Map && ((Map<String, Object>) value).containsKey("@value")) {
1487//             value = ((Map<String, Object>) value).get("@value");
1488//         }
1489//         if (value instanceof Boolean) {
1490//             return (Boolean) value;
1491//         }
1492//         return thedefault;
1493//     }
1494
1495//     /**
1496//      * Removes an existing embed.
1497//      *
1498//      * @param state
1499//      *            the current framing state.
1500//      * @param id
1501//      *            the @id of the embed to remove.
1502//      */
1503//     private static void removeEmbed(FramingContext state, String id) {
1504//         // get existing embed
1505//         final Map<String, EmbedNode> embeds = state.embeds;
1506//         final EmbedNode embed = embeds.get(id);
1507//         final Object parent = embed.parent;
1508//         final String property = embed.property;
1509
1510//         // create reference to replace embed
1511//         final Map<String, Object> node = new LinkedHashMap<String, Object>();
1512//         node.put("@id", id);
1513
1514//         // remove existing embed
1515//         if (JsonLdUtils.isNode(parent)) {
1516//             // replace subject with reference
1517//             final List<Object> newvals = new ArrayList<Object>();
1518//             final List<Object> oldvals = (List<Object>) ((Map<String, Object>) parent)
1519//                     .get(property);
1520//             for (final Object v : oldvals) {
1521//                 if (v instanceof Map && Obj.equals(((Map<String, Object>) v).get("@id"), id)) {
1522//                     newvals.add(node);
1523//                 } else {
1524//                     newvals.add(v);
1525//                 }
1526//             }
1527//             ((Map<String, Object>) parent).put(property, newvals);
1528//         }
1529//         // recursively remove dependent dangling embeds
1530//         removeDependents(embeds, id);
1531//     }
1532
1533//     private static void removeDependents(Map<String, EmbedNode> embeds, String id) {
1534//         // get embed keys as a separate array to enable deleting keys in map
1535//         for (final String id_dep : embeds.keySet()) {
1536//             final EmbedNode e = embeds.get(id_dep);
1537//             final Object p = e.parent != null ? e.parent : new LinkedHashMap<String, Object>();
1538//             if (!(p instanceof Map)) {
1539//                 continue;
1540//             }
1541//             final String pid = (String) ((Map<String, Object>) p).get("@id");
1542//             if (Obj.equals(id, pid)) {
1543//                 embeds.remove(id_dep);
1544//                 removeDependents(embeds, id_dep);
1545//             }
1546//         }
1547//     }
1548
1549//     private Map<String, Object> filterNodes(FramingContext state, Map<String, Object> nodes,
1550//             Map<String, Object> frame) throws JsonLdError {
1551//         final Map<String, Object> rval = new LinkedHashMap<String, Object>();
1552//         for (final String id : nodes.keySet()) {
1553//             final Map<String, Object> element = (Map<String, Object>) nodes.get(id);
1554//             if (element != null && filterNode(state, element, frame)) {
1555//                 rval.put(id, element);
1556//             }
1557//         }
1558//         return rval;
1559//     }
1560
1561//     private boolean filterNode(FramingContext state, Map<String, Object> node,
1562//             Map<String, Object> frame) throws JsonLdError {
1563//         final Object types = frame.get("@type");
1564//         if (types != null) {
1565//             if (!(types instanceof List)) {
1566//                 throw new JsonLdError(Error.SYNTAX_ERROR, "frame @type must be an array");
1567//             }
1568//             Object nodeTypes = node.get("@type");
1569//             if (nodeTypes == null) {
1570//                 nodeTypes = new ArrayList<Object>();
1571//             } else if (!(nodeTypes instanceof List)) {
1572//                 throw new JsonLdError(Error.SYNTAX_ERROR, "node @type must be an array");
1573//             }
1574//             if (((List<Object>) types).size() == 1 && ((List<Object>) types).get(0) instanceof Map
1575//                     && ((Map<String, Object>) ((List<Object>) types).get(0)).size() == 0) {
1576//                 return !((List<Object>) nodeTypes).isEmpty();
1577//             } else {
1578//                 for (final Object i : (List<Object>) nodeTypes) {
1579//                     for (final Object j : (List<Object>) types) {
1580//                         if (JsonLdUtils.deepCompare(i, j)) {
1581//                             return true;
1582//                         }
1583//                     }
1584//                 }
1585//                 return false;
1586//             }
1587//         } else {
1588//             for (final String key : frame.keySet()) {
1589//                 if ("@id".equals(key) || !isKeyword(key) && !(node.containsKey(key))) {
1590//                     return false;
1591//                 }
1592//             }
1593//             return true;
1594//         }
1595//     }
1596
1597//     /**
1598//      * Adds framing output to the given parent.
1599//      *
1600//      * @param state
1601//      *            the current framing state.
1602//      * @param parent
1603//      *            the parent to add to.
1604//      * @param property
1605//      *            the parent property.
1606//      * @param output
1607//      *            the output to add.
1608//      */
1609//     private static void addFrameOutput(FramingContext state, Object parent, String property,
1610//             Object output) {
1611//         if (parent instanceof Map) {
1612//             List<Object> prop = (List<Object>) ((Map<String, Object>) parent).get(property);
1613//             if (prop == null) {
1614//                 prop = new ArrayList<Object>();
1615//                 ((Map<String, Object>) parent).put(property, prop);
1616//             }
1617//             prop.add(output);
1618//         } else {
1619//             ((List) parent).add(output);
1620//         }
1621//     }
1622
1623//     /**
1624//      * Embeds values for the given subject and property into the given output
1625//      * during the framing algorithm.
1626//      *
1627//      * @param state
1628//      *            the current framing state.
1629//      * @param element
1630//      *            the subject.
1631//      * @param property
1632//      *            the property.
1633//      * @param output
1634//      *            the output.
1635//      */
1636//     private void embedValues(FramingContext state, Map<String, Object> element, String property,
1637//             Object output) {
1638//         // embed subject properties in output
1639//         final List<Object> objects = (List<Object>) element.get(property);
1640//         for (Object o : objects) {
1641//             // handle subject reference
1642//             if (JsonLdUtils.isNodeReference(o)) {
1643//                 final String sid = (String) ((Map<String, Object>) o).get("@id");
1644
1645//                 // embed full subject if isn't already embedded
1646//                 if (!state.embeds.containsKey(sid)) {
1647//                     // add embed
1648//                     final EmbedNode embed = new EmbedNode();
1649//                     embed.parent = output;
1650//                     embed.property = property;
1651//                     state.embeds.put(sid, embed);
1652
1653//                     // recurse into subject
1654//                     o = new LinkedHashMap<String, Object>();
1655//                     Map<String, Object> s = (Map<String, Object>) this.nodeMap.get(sid);
1656//                     if (s == null) {
1657//                         s = new LinkedHashMap<String, Object>();
1658//                         s.put("@id", sid);
1659//                     }
1660//                     for (final String prop : s.keySet()) {
1661//                         // copy keywords
1662//                         if (isKeyword(prop)) {
1663//                             ((Map<String, Object>) o).put(prop, JsonLdUtils.clone(s.get(prop)));
1664//                             continue;
1665//                         }
1666//                         embedValues(state, s, prop, o);
1667//                     }
1668//                 }
1669//                 addFrameOutput(state, output, property, o);
1670//             }
1671//             // copy non-subject value
1672//             else {
1673//                 addFrameOutput(state, output, property, JsonLdUtils.clone(o));
1674//             }
1675//         }
1676//     }
1677
1678//     /***
1679//      * ____ _ __ ____ ____ _____ _ _ _ _ _ / ___|___ _ ____ _____ _ __| |_ / _|_
1680//      * __ ___ _ __ ___ | _ \| _ \| ___| / \ | | __ _ ___ _ __(_) |_| |__ _ __
1681//      * ___ | | / _ \| '_ \ \ / / _ \ '__| __| | |_| '__/ _ \| '_ ` _ \ | |_) | |
1682//      * | | |_ / _ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \ | |__| (_) | | | \
1683//      * V / __/ | | |_ | _| | | (_) | | | | | | | _ <| |_| | _| / ___ \| | (_| |
1684//      * (_) | | | | |_| | | | | | | | | \____\___/|_| |_|\_/ \___|_| \__| |_| |_|
1685//      * \___/|_| |_| |_| |_| \_\____/|_| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_|
1686//      * |_| |_| |___/
1687//      */
1688
1689//     /**
1690//      * Helper class for node usages
1691//      *
1692//      * @author tristan
1693//      */
1694//     private class UsagesNode {
1695//         public UsagesNode(NodeMapNode node, String property, Map<String, Object> value) {
1696//             this.node = node;
1697//             this.property = property;
1698//             this.value = value;
1699//         }
1700
1701//         public NodeMapNode node = null;
1702//         public String property = null;
1703//         public Map<String, Object> value = null;
1704//     }
1705
1706//     private class NodeMapNode extends LinkedHashMap<String, Object> {
1707//         public List<UsagesNode> usages = new ArrayList();
1708
1709//         public NodeMapNode(String id) {
1710//             super();
1711//             this.put("@id", id);
1712//         }
1713
1714//         // helper fucntion for 4.3.3
1715//         public boolean isWellFormedListNode() {
1716//             if (usages.size() != 1) {
1717//                 return false;
1718//             }
1719//             int keys = 0;
1720//             if (containsKey(RDF_FIRST)) {
1721//                 keys++;
1722//                 if (!(get(RDF_FIRST) instanceof List && ((List<Object>) get(RDF_FIRST)).size() == 1)) {
1723//                     return false;
1724//                 }
1725//             }
1726//             if (containsKey(RDF_REST)) {
1727//                 keys++;
1728//                 if (!(get(RDF_REST) instanceof List && ((List<Object>) get(RDF_REST)).size() == 1)) {
1729//                     return false;
1730//                 }
1731//             }
1732//             if (containsKey("@type")) {
1733//                 keys++;
1734//                 if (!(get("@type") instanceof List && ((List<Object>) get("@type")).size() == 1)
1735//                         && RDF_LIST.equals(((List<Object>) get("@type")).get(0))) {
1736//                     return false;
1737//                 }
1738//             }
1739//             // TODO: SPEC: 4.3.3 has no mention of @id
1740//             if (containsKey("@id")) {
1741//                 keys++;
1742//             }
1743//             if (keys < size()) {
1744//                 return false;
1745//             }
1746//             return true;
1747//         }
1748
1749//         // return this node without the usages variable
1750//         public Map<String, Object> serialize() {
1751//             return new LinkedHashMap<String, Object>(this);
1752//         }
1753//     }
1754
1755//     /**
1756//      * Converts RDF statements into JSON-LD.
1757//      *
1758//      * @param dataset
1759//      *            the RDF statements.
1760//      * @return A list of JSON-LD objects found in the given dataset.
1761//      * @throws JsonLdError
1762//      *             If there was an error during conversion from RDF to JSON-LD.
1763//      */
1764//     public List<Object> fromRDF(final RDFDataset dataset) throws JsonLdError {
1765//         // 1)
1766//         final Map<String, NodeMapNode> defaultGraph = new LinkedHashMap<String, NodeMapNode>();
1767//         // 2)
1768//         final Map<String, Map<String, NodeMapNode>> graphMap = new LinkedHashMap<String, Map<String, NodeMapNode>>();
1769//         graphMap.put("@default", defaultGraph);
1770
1771//         // 3/3.1)
1772//         for (final String name : dataset.graphNames()) {
1773
1774//             final List<RDFDataset.Quad> graph = dataset.getQuads(name);
1775
1776//             // 3.2+3.4)
1777//             Map<String, NodeMapNode> nodeMap;
1778//             if (!graphMap.containsKey(name)) {
1779//                 nodeMap = new LinkedHashMap<String, NodeMapNode>();
1780//                 graphMap.put(name, nodeMap);
1781//             } else {
1782//                 nodeMap = graphMap.get(name);
1783//             }
1784
1785//             // 3.3)
1786//             if (!"@default".equals(name) && !Obj.contains(defaultGraph, name)) {
1787//                 defaultGraph.put(name, new NodeMapNode(name));
1788//             }
1789
1790//             // 3.5)
1791//             for (final RDFDataset.Quad triple : graph) {
1792//                 final String subject = triple.getSubject().getValue();
1793//                 final String predicate = triple.getPredicate().getValue();
1794//                 final RDFDataset.Node object = triple.getObject();
1795
1796//                 // 3.5.1+3.5.2)
1797//                 NodeMapNode node;
1798//                 if (!nodeMap.containsKey(subject)) {
1799//                     node = new NodeMapNode(subject);
1800//                     nodeMap.put(subject, node);
1801//                 } else {
1802//                     node = nodeMap.get(subject);
1803//                 }
1804
1805//                 // 3.5.3)
1806//                 if ((object.isIRI() || object.isBlankNode())
1807//                         && !nodeMap.containsKey(object.getValue())) {
1808//                     nodeMap.put(object.getValue(), new NodeMapNode(object.getValue()));
1809//                 }
1810
1811//                 // 3.5.4)
1812//                 if (RDF_TYPE.equals(predicate) && (object.isIRI() || object.isBlankNode())
1813//                         && !opts.getUseRdfType()) {
1814//                     JsonLdUtils.mergeValue(node, "@type", object.getValue());
1815//                     continue;
1816//                 }
1817
1818//                 // 3.5.5)
1819//                 final Map<String, Object> value = object.toObject(opts.getUseNativeTypes());
1820
1821//                 // 3.5.6+7)
1822//                 JsonLdUtils.mergeValue(node, predicate, value);
1823
1824//                 // 3.5.8)
1825//                 if (object.isBlankNode() || object.isIRI()) {
1826//                     // 3.5.8.1-3)
1827//                     nodeMap.get(object.getValue()).usages
1828//                             .add(new UsagesNode(node, predicate, value));
1829//                 }
1830//             }
1831//         }
1832
1833//         // 4)
1834//         for (final String name : graphMap.keySet()) {
1835//             final Map<String, NodeMapNode> graph = graphMap.get(name);
1836
1837//             // 4.1)
1838//             if (!graph.containsKey(RDF_NIL)) {
1839//                 continue;
1840//             }
1841
1842//             // 4.2)
1843//             final NodeMapNode nil = graph.get(RDF_NIL);
1844//             // 4.3)
1845//             for (final UsagesNode usage : nil.usages) {
1846//                 // 4.3.1)
1847//                 NodeMapNode node = usage.node;
1848//                 String property = usage.property;
1849//                 Map<String, Object> head = usage.value;
1850//                 // 4.3.2)
1851//                 final List<Object> list = new ArrayList<Object>();
1852//                 final List<String> listNodes = new ArrayList<String>();
1853//                 // 4.3.3)
1854//                 while (RDF_REST.equals(property) && node.isWellFormedListNode()) {
1855//                     // 4.3.3.1)
1856//                     list.add(((List<Object>) node.get(RDF_FIRST)).get(0));
1857//                     // 4.3.3.2)
1858//                     listNodes.add((String) node.get("@id"));
1859//                     // 4.3.3.3)
1860//                     final UsagesNode nodeUsage = node.usages.get(0);
1861//                     // 4.3.3.4)
1862//                     node = nodeUsage.node;
1863//                     property = nodeUsage.property;
1864//                     head = nodeUsage.value;
1865//                     // 4.3.3.5)
1866//                     if (!JsonLdUtils.isBlankNode(node)) {
1867//                         break;
1868//                     }
1869//                 }
1870//                 // 4.3.4)
1871//                 if (RDF_FIRST.equals(property)) {
1872//                     // 4.3.4.1)
1873//                     if (RDF_NIL.equals(node.get("@id"))) {
1874//                         continue;
1875//                     }
1876//                     // 4.3.4.3)
1877//                     final String headId = (String) head.get("@id");
1878//                     // 4.3.4.4-5)
1879//                     head = (Map<String, Object>) ((List<Object>) graph.get(headId).get(RDF_REST))
1880//                             .get(0);
1881//                     // 4.3.4.6)
1882//                     list.remove(list.size() - 1);
1883//                     listNodes.remove(listNodes.size() - 1);
1884//                 }
1885//                 // 4.3.5)
1886//                 head.remove("@id");
1887//                 // 4.3.6)
1888//                 Collections.reverse(list);
1889//                 // 4.3.7)
1890//                 head.put("@list", list);
1891//                 // 4.3.8)
1892//                 for (final String nodeId : listNodes) {
1893//                     graph.remove(nodeId);
1894//                 }
1895//             }
1896//         }
1897
1898//         // 5)
1899//         final List<Object> result = new ArrayList<Object>();
1900//         // 6)
1901//         final List<String> ids = new ArrayList<String>(defaultGraph.keySet());
1902//         Collections.sort(ids);
1903//         for (final String subject : ids) {
1904//             final NodeMapNode node = defaultGraph.get(subject);
1905//             // 6.1)
1906//             if (graphMap.containsKey(subject)) {
1907//                 // 6.1.1)
1908//                 node.put("@graph", new ArrayList<Object>());
1909//                 // 6.1.2)
1910//                 final List<String> keys = new ArrayList<String>(graphMap.get(subject).keySet());
1911//                 Collections.sort(keys);
1912//                 for (final String s : keys) {
1913//                     final NodeMapNode n = graphMap.get(subject).get(s);
1914//                     if (n.size() == 1 && n.containsKey("@id")) {
1915//                         continue;
1916//                     }
1917//                     ((List<Object>) node.get("@graph")).add(n.serialize());
1918//                 }
1919//             }
1920//             // 6.2)
1921//             if (node.size() == 1 && node.containsKey("@id")) {
1922//                 continue;
1923//             }
1924//             result.add(node.serialize());
1925//         }
1926
1927//         return result;
1928//     }
1929
1930//     /***
1931//      * ____ _ _ ____ ____ _____ _ _ _ _ _ / ___|___ _ ____ _____ _ __| |_ | |_
1932//      * ___ | _ \| _ \| ___| / \ | | __ _ ___ _ __(_) |_| |__ _ __ ___ | | / _ \|
1933//      * '_ \ \ / / _ \ '__| __| | __/ _ \ | |_) | | | | |_ / _ \ | |/ _` |/ _ \|
1934//      * '__| | __| '_ \| '_ ` _ \ | |__| (_) | | | \ V / __/ | | |_ | || (_) | |
1935//      * _ <| |_| | _| / ___ \| | (_| | (_) | | | | |_| | | | | | | | |
1936//      * \____\___/|_| |_|\_/ \___|_| \__| \__\___/ |_| \_\____/|_| /_/ \_\_|\__,
1937//      * |\___/|_| |_|\__|_| |_|_| |_| |_| |___/
1938//      */
1939
1940//     /**
1941//      * Adds RDF triples for each graph in the current node map to an RDF
1942//      * dataset.
1943//      *
1944//      * @return the RDF dataset.
1945//      * @throws JsonLdError
1946//      *             If there was an error converting from JSON-LD to RDF.
1947//      */
1948//     public RDFDataset toRDF() throws JsonLdError {
1949//         // TODO: make the default generateNodeMap call (i.e. without a
1950//         // graphName) create and return the nodeMap
1951//         final Map<String, Object> nodeMap = new LinkedHashMap<String, Object>();
1952//         nodeMap.put("@default", new LinkedHashMap<String, Object>());
1953//         generateNodeMap(this.value, nodeMap);
1954
1955//         final RDFDataset dataset = new RDFDataset(this);
1956
1957//         for (final String graphName : nodeMap.keySet()) {
1958//             // 4.1)
1959//             if (JsonLdUtils.isRelativeIri(graphName)) {
1960//                 continue;
1961//             }
1962//             final Map<String, Object> graph = (Map<String, Object>) nodeMap.get(graphName);
1963//             dataset.graphToRDF(graphName, graph);
1964//         }
1965
1966//         return dataset;
1967//     }
1968
1969//     /***
1970//      * _ _ _ _ _ _ _ _ _ _ _ | \ | | ___ _ __ _ __ ___ __ _| (_)______ _| |_(_)
1971//      * ___ _ __ / \ | | __ _ ___ _ __(_) |_| |__ _ __ ___ | \| |/ _ \| '__| '_ `
1972//      * _ \ / _` | | |_ / _` | __| |/ _ \| '_ \ / _ \ | |/ _` |/ _ \| '__| | __|
1973//      * '_ \| '_ ` _ \ | |\ | (_) | | | | | | | | (_| | | |/ / (_| | |_| | (_) |
1974//      * | | | / ___ \| | (_| | (_) | | | | |_| | | | | | | | | |_| \_|\___/|_|
1975//      * |_| |_| |_|\__,_|_|_/___\__,_|\__|_|\___/|_| |_| /_/ \_\_|\__, |\___/|_|
1976//      * |_|\__|_| |_|_| |_| |_| |___/
1977//      */
1978
1979//     /**
1980//      * Performs RDF normalization on the given JSON-LD input.
1981//      *
1982//      * @param dataset
1983//      *            the expanded JSON-LD object to normalize.
1984//      * @return The normalized JSON-LD object
1985//      * @throws JsonLdError
1986//      *             If there was an error while normalizing.
1987//      */
1988//     public Object normalize(Map<String, Object> dataset) throws JsonLdError {
1989//         // create quads and map bnodes to their associated quads
1990//         final List<Object> quads = new ArrayList<Object>();
1991//         final Map<String, Object> bnodes = new LinkedHashMap<String, Object>();
1992//         for (String graphName : dataset.keySet()) {
1993//             final List<Map<String, Object>> triples = (List<Map<String, Object>>) dataset
1994//                     .get(graphName);
1995//             if ("@default".equals(graphName)) {
1996//                 graphName = null;
1997//             }
1998//             for (final Map<String, Object> quad : triples) {
1999//                 if (graphName != null) {
2000//                     if (graphName.indexOf("_:") == 0) {
2001//                         final Map<String, Object> tmp = new LinkedHashMap<String, Object>();
2002//                         tmp.put("type", "blank node");
2003//                         tmp.put("value", graphName);
2004//                         quad.put("name", tmp);
2005//                     } else {
2006//                         final Map<String, Object> tmp = new LinkedHashMap<String, Object>();
2007//                         tmp.put("type", "IRI");
2008//                         tmp.put("value", graphName);
2009//                         quad.put("name", tmp);
2010//                     }
2011//                 }
2012//                 quads.add(quad);
2013
2014//                 final String[] attrs = new String[] { "subject", "object", "name" };
2015//                 for (final String attr : attrs) {
2016//                     if (quad.containsKey(attr)
2017//                             && "blank node".equals(((Map<String, Object>) quad.get(attr))
2018//                                     .get("type"))) {
2019//                         final String id = (String) ((Map<String, Object>) quad.get(attr))
2020//                                 .get("value");
2021//                         if (!bnodes.containsKey(id)) {
2022//                             bnodes.put(id, new LinkedHashMap<String, List<Object>>() {
2023//                                 {
2024//                                     put("quads", new ArrayList<Object>());
2025//                                 }
2026//                             });
2027//                         }
2028//                         ((List<Object>) ((Map<String, Object>) bnodes.get(id)).get("quads"))
2029//                                 .add(quad);
2030//                     }
2031//                 }
2032//             }
2033//         }
2034
2035//         // mapping complete, start canonical naming
2036//         final NormalizeUtils normalizeUtils = new NormalizeUtils(quads, bnodes, new UniqueNamer(
2037//                 "_:c14n"), opts);
2038//         return normalizeUtils.hashBlankNodes(bnodes.keySet());
2039//     }
2040
2041// }
2042