private void frame(FramingContext state, Map<String, Object> nodes, Map<String, Object> frame,
            Object parent, String property) throws JsonLdError {

        // https://json-ld.org/spec/latest/json-ld-framing/#framing-algorithm

        // 2.
        // Initialize flags embed, explicit, and requireAll from object embed
        // flag,
        // explicit inclusion flag, and require all flag in state overriding
        // from
        // any property values for @embed, @explicit, and @requireAll in frame.
        // TODO: handle @requireAll
        final Embed embed = getFrameEmbed(frame, state.embed);
        final Boolean explicitOn = getFrameFlag(frame, JsonLdConsts.EXPLICIT, state.explicit);
        final Boolean requireAll = getFrameFlag(frame, JsonLdConsts.REQUIRE_ALL, state.requireAll);
        final Map<String, Object> flags = newMap();
        flags.put(JsonLdConsts.EXPLICIT, explicitOn);
        flags.put(JsonLdConsts.EMBED, embed);
        flags.put(JsonLdConsts.REQUIRE_ALL, requireAll);

        // 3.
        // Create a list of matched subjects by filtering subjects against frame
        // using the Frame Matching algorithm with state, subjects, frame, and
        // requireAll.
        final Map<String, Object> matches = filterNodes(state, nodes, frame, requireAll);
        final List<String> ids = new ArrayList<String>(matches.keySet());
        Collections.sort(ids);

        // 4.
        // Set link the the value of link in state associated with graph name in
        // state,
        // creating a new empty dictionary, if necessary.
        final Map<String, EmbedNode> link = state.uniqueEmbeds;

        // 5.
        // For each id and associated node object node from the set of matched
        // subjects, ordered by id:
        for (final String id : ids) {
            final Map<String, Object> subject = (Map<String, Object>) matches.get(id);

            // 5.1
            // Initialize output to a new dictionary with @id and id and add
            // output to link associated with id.
            final Map<String, Object> output = newMap();
            output.put(JsonLdConsts.ID, id);

            // 5.2
            // If embed is @link and id is in link, node already exists in
            // results.
            // Add the associated node object from link to parent and do not
            // perform
            // additional processing for this node.
            if (embed == Embed.LINK && state.uniqueEmbeds.containsKey(id)) {
                addFrameOutput(state, parent, property, state.uniqueEmbeds.get(id));
                continue;
            }

            // Occurs only at top level, compartmentalize each top-level match
            if (property == null) {
                state.uniqueEmbeds = new HashMap<>();
            }

            // 5.3
            // Otherwise, if embed is @never or if a circular reference would be
            // created by an embed,
            // add output to parent and do not perform additional processing for
            // this node.
            if (embed == Embed.NEVER || createsCircularReference(id, state)) {
                addFrameOutput(state, parent, property, output);
                continue;
            }

            // 5.4
            // Otherwise, if embed is @last, remove any existing embedded node
            // from parent associated
            // with graph name in state. Requires sorting of subjects.
            if (embed == Embed.LAST) {
                if (state.uniqueEmbeds.containsKey(id)) {
                    removeEmbed(state, id);
                }
                state.uniqueEmbeds.put(id, new EmbedNode(parent, property));
            }

            state.subjectStack.push(id);

            // 5.5 If embed is @last or @always

            // Skip 5.5.1

            // 5.5.2 For each property and objects in node, ordered by property:
            final Map<String, Object> element = (Map<String, Object>) matches.get(id);
            List<String> props = new ArrayList<String>(element.keySet());
            Collections.sort(props);
            for (final String prop : props) {

                // 5.5.2.1 If property is a keyword, add property and objects to
                // output.
                if (isKeyword(prop)) {
                    output.put(prop, JsonLdUtils.clone(element.get(prop)));
                    continue;
                }

                // 5.5.2.2 Otherwise, if property is not in frame, and explicit
                // is true, processors
                // MUST NOT add any values for property to output, and the
                // following steps are skipped.
                if (explicitOn && !frame.containsKey(prop)) {
                    continue;
                }

                // add objects
                final List<Object> value = (List<Object>) element.get(prop);

                // 5.5.2.3 For each item in objects:
                for (final Object item : value) {
                    if ((item instanceof Map)
                            && ((Map<String, Object>) item).containsKey(JsonLdConsts.LIST)) {
                        // add empty list
                        final Map<String, Object> list = newMap();
                        list.put(JsonLdConsts.LIST, new ArrayList<Object>());
                        addFrameOutput(state, output, prop, list);

                        // add list objects
                        for (final Object listitem : (List<Object>) ((Map<String, Object>) item)
                                .get(JsonLdConsts.LIST)) {
                            // 5.5.2.3.1.1 recurse into subject reference
                            if (JsonLdUtils.isNodeReference(listitem)) {
                                final Map<String, Object> tmp = newMap();
                                final String itemid = (String) ((Map<String, Object>) listitem)
                                        .get(JsonLdConsts.ID);
                                // TODO: nodes may need to be node_map,
                                // which is global
                                tmp.put(itemid, this.nodeMap.get(itemid));
                                Map<String, Object> subframe;
                                if (frame.containsKey(prop)) {
                                    subframe = (Map<String, Object>) ((List<Object>) frame
                                            .get(prop)).get(0);
                                } else {
                                    subframe = flags;
                                }
                                frame(state, tmp, subframe, list, JsonLdConsts.LIST);
                            } else {

                                // include other values automatcially (TODO:
                                // may need JsonLdUtils.clone(n))
                                addFrameOutput(state, list, JsonLdConsts.LIST, listitem);
                            }
                        }
                    }
                    // recurse into subject reference
                    else if (JsonLdUtils.isNodeReference(item)) {
                        final Map<String, Object> tmp = newMap();
                        final String itemid = (String) ((Map<String, Object>) item)
                                .get(JsonLdConsts.ID);
                        // TODO: nodes may need to be node_map, which is
                        // global
                        tmp.put(itemid, this.nodeMap.get(itemid));
                        Map<String, Object> subframe;
                        if (frame.containsKey(prop)) {
                            subframe = (Map<String, Object>) ((List<Object>) frame.get(prop))
                                    .get(0);
                        } else {
                            subframe = flags;
                        }
                        frame(state, tmp, subframe, output, prop);
                    } else {
                        // include other values automatically (TODO: may
                        // need JsonLdUtils.clone(o))
                        addFrameOutput(state, output, prop, item);
                    }
                }
            }

            // handle defaults
            props = new ArrayList<String>(frame.keySet());
            Collections.sort(props);
            for (final String prop : props) {
                // skip keywords
                if (isKeyword(prop)) {
                    continue;
                }

                final List<Object> pf = (List<Object>) frame.get(prop);
                Map<String, Object> propertyFrame = pf.size() > 0 ? (Map<String, Object>) pf.get(0)
                        : null;
                if (propertyFrame == null) {
                    propertyFrame = newMap();
                }
                final boolean omitDefaultOn = getFrameFlag(propertyFrame, JsonLdConsts.OMIT_DEFAULT,
                        state.omitDefault);
                if (!omitDefaultOn && !output.containsKey(prop)) {
                    Object def = "@null";
                    if (propertyFrame.containsKey(JsonLdConsts.DEFAULT)) {
                        def = JsonLdUtils.clone(propertyFrame.get(JsonLdConsts.DEFAULT));
                    }
                    if (!(def instanceof List)) {
                        final List<Object> tmp = new ArrayList<Object>();
                        tmp.add(def);
                        def = tmp;
                    }
                    final Map<String, Object> tmp1 = newMap(JsonLdConsts.PRESERVE, def);
                    final List<Object> tmp2 = new ArrayList<Object>();
                    tmp2.add(tmp1);
                    output.put(prop, tmp2);
                }
            }

            // add output to parent
            addFrameOutput(state, parent, property, output);

            state.subjectStack.pop();
        }
    }
private void frame ( FramingContext state , Map < String , Object > nodes , Map < String , Object > frame , Object parent , String property ) throws JsonLdError { final Embed embed = getFrameEmbed ( frame , state . embed ) ; final Boolean explicitOn = getFrameFlag ( frame , JsonLdConsts . EXPLICIT , state . explicit ) ; final Boolean requireAll = getFrameFlag ( frame , JsonLdConsts . REQUIRE_ALL , state . requireAll ) ; final Map < String , Object > flags = newMap ( ) ; flags . put ( JsonLdConsts . EXPLICIT , explicitOn ) ; flags . put ( JsonLdConsts . EMBED , embed ) ; flags . put ( JsonLdConsts . REQUIRE_ALL , requireAll ) ; final Map < String , Object > matches = filterNodes ( state , nodes , frame , requireAll ) ; final List < String > ids = new ArrayList < String > ( matches . keySet ( ) ) ; Collections . sort ( ids ) ; final Map < String , EmbedNode > link = state . uniqueEmbeds ; for ( final String id : ids ) { final Map < String , Object > subject = ( Map < String , Object > ) matches . get ( id ) ; final Map < String , Object > output = newMap ( ) ; output . put ( JsonLdConsts . ID , id ) ; if ( embed == Embed . LINK && state . uniqueEmbeds . containsKey ( id ) ) { addFrameOutput ( state , parent , property , state . uniqueEmbeds . get ( id ) ) ; continue ; } if ( property == null ) { state . uniqueEmbeds = new HashMap < > ( ) ; } if ( embed == Embed . NEVER || createsCircularReference ( id , state ) ) { addFrameOutput ( state , parent , property , output ) ; continue ; } if ( embed == Embed . LAST ) { if ( state . uniqueEmbeds . containsKey ( id ) ) { removeEmbed ( state , id ) ; } state . uniqueEmbeds . put ( id , new EmbedNode ( parent , property ) ) ; } state . subjectStack . push ( id ) ; final Map < String , Object > element = ( Map < String , Object > ) matches . get ( id ) ; List < String > props = new ArrayList < String > ( element . keySet ( ) ) ; Collections . sort ( props ) ; for ( final String prop : props ) { if ( isKeyword ( prop ) ) { output . put ( prop , JsonLdUtils . clone ( element . get ( prop ) ) ) ; continue ; } if ( explicitOn && ! frame . containsKey ( prop ) ) { continue ; } final List < Object > value = ( List < Object > ) element . get ( prop ) ; for ( final Object item : value ) { if ( ( item instanceof Map ) && ( ( Map < String , Object > ) item ) . containsKey ( JsonLdConsts . LIST ) ) { final Map < String , Object > list = newMap ( ) ; list . put ( JsonLdConsts . LIST , new ArrayList < Object > ( ) ) ; addFrameOutput ( state , output , prop , list ) ; for ( final Object listitem : ( List < Object > ) ( ( Map < String , Object > ) item ) . get ( JsonLdConsts . LIST ) ) { if ( JsonLdUtils . isNodeReference ( listitem ) ) { final Map < String , Object > tmp = newMap ( ) ; final String itemid = ( String ) ( ( Map < String , Object > ) listitem ) . get ( JsonLdConsts . ID ) ; tmp . put ( itemid , this . nodeMap . get ( itemid ) ) ; Map < String , Object > subframe ; if ( frame . containsKey ( prop ) ) { subframe = ( Map < String , Object > ) ( ( List < Object > ) frame . get ( prop ) ) . get ( 0 ) ; } else { subframe = flags ; } frame ( state , tmp , subframe , list , JsonLdConsts . LIST ) ; } else { addFrameOutput ( state , list , JsonLdConsts . LIST , listitem ) ; } } } else if ( JsonLdUtils . isNodeReference ( item ) ) { final Map < String , Object > tmp = newMap ( ) ; final String itemid = ( String ) ( ( Map < String , Object > ) item ) . get ( JsonLdConsts . ID ) ; tmp . put ( itemid , this . nodeMap . get ( itemid ) ) ; Map < String , Object > subframe ; if ( frame . containsKey ( prop ) ) { subframe = ( Map < String , Object > ) ( ( List < Object > ) frame . get ( prop ) ) . get ( 0 ) ; } else { subframe = flags ; } frame ( state , tmp , subframe , output , prop ) ; } else { addFrameOutput ( state , output , prop , item ) ; } } } props = new ArrayList < String > ( frame . keySet ( ) ) ; Collections . sort ( props ) ; for ( final String prop : props ) { if ( isKeyword ( prop ) ) { continue ; } final List < Object > pf = ( List < Object > ) frame . get ( prop ) ; Map < String , Object > propertyFrame = pf . size ( ) > 0 ? ( Map < String , Object > ) pf . get ( 0 ) : null ; if ( propertyFrame == null ) { propertyFrame = newMap ( ) ; } final boolean omitDefaultOn = getFrameFlag ( propertyFrame , JsonLdConsts . OMIT_DEFAULT , state . omitDefault ) ; if ( ! omitDefaultOn && ! output . containsKey ( prop ) ) { Object def = "@null" ; if ( propertyFrame . containsKey ( JsonLdConsts . DEFAULT ) ) { def = JsonLdUtils . clone ( propertyFrame . get ( JsonLdConsts . DEFAULT ) ) ; } if ( ! ( def instanceof List ) ) { final List < Object > tmp = new ArrayList < Object > ( ) ; tmp . add ( def ) ; def = tmp ; } final Map < String , Object > tmp1 = newMap ( JsonLdConsts . PRESERVE , def ) ; final List < Object > tmp2 = new ArrayList < Object > ( ) ; tmp2 . add ( tmp1 ) ; output . put ( prop , tmp2 ) ; } } addFrameOutput ( state , parent , property , output ) ; state . subjectStack . pop ( ) ; } }
Frames subjects according to the given frame.

@param state
the current framing state.
@param frame
the frame.
@param parent
the parent subject or top-level array.
@param property
the parent property, initialized to null.
@throws JsonLdError
If there was an error during framing.