CQueryLiteral.java

/*
 * @cond LICENSE
 * ######################################################################################
 * # LGPL License                                                                       #
 * #                                                                                    #
 * # This file is part of the LightJason AgentSpeak(L++)                                #
 * # Copyright (c) 2015-19, LightJason (info@lightjason.org)                            #
 * # This program is free software: you can redistribute it and/or modify               #
 * # it under the terms of the GNU Lesser General Public License as                     #
 * # published by the Free Software Foundation, either version 3 of the                 #
 * # License, or (at your option) any later version.                                    #
 * #                                                                                    #
 * # This program is distributed in the hope that it will be useful,                    #
 * # but WITHOUT ANY WARRANTY; without even the implied warranty of                     #
 * # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                      #
 * # GNU Lesser General Public License for more details.                                #
 * #                                                                                    #
 * # You should have received a copy of the GNU Lesser General Public License           #
 * # along with this program. If not, see http://www.gnu.org/licenses/                  #
 * ######################################################################################
 * @endcond
 */

package org.lightjason.agentspeak.action.builtin.web.graphql;

import org.lightjason.agentspeak.language.ILiteral;
import org.lightjason.agentspeak.language.IRawTerm;
import org.lightjason.agentspeak.language.ITerm;

import javax.annotation.Nonnull;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * action to run graphql query by a literal.
 * The calls the data of a graphql service and returns a literal
 * based on the query result, the input argument is at the first
 * position the graphql service url, the second argument a literal, which
 * defines the query structure and all other arguments the literal functor structure.
 * The action fails on connection and parsing errors.
 *
 * {@code L = generic/type/parseliteral( "allUsers(id, firstName, lastName)" ); GQ = web/graphql/queryliteral( "https://fakerql.com/graphql", L, "graphql-fake" );}
 * @see http://graphql.org/
 */
public final class CQueryLiteral extends IBaseGraphQL
{
    /**
     * serial id
     */
    private static final long serialVersionUID = -6514047475694739845L;

    /**
     * ctor
     */
    public CQueryLiteral()
    {
        super( 3 );
    }

    @Override
    protected final String argumentquery( @Nonnull final ITerm p_argument )
    {
        return MessageFormat.format( "'{'{0}'}'", root( p_argument.raw() ) );
    }

    /**
     * converts a literal query structure
     *
     * @param p_literal literal
     * @return query string
     */
    @Nonnull
    private static String root( @Nonnull final ILiteral p_literal )
    {
        if ( p_literal.emptyValues() )
            return p_literal.functor();

        final String l_query = values( p_literal.values() );
        final String l_fields = fields( p_literal.values() );

        return MessageFormat.format(
            "{0}{1}{2}",
            p_literal.functor(), l_query.isEmpty() ? "" : MessageFormat.format( "({0})", l_query ),
            l_fields.isEmpty() ? "" : MessageFormat.format( "'{'{0}'}'", l_fields )
        );
    }

    /**
     * argument values
     *
     * @param p_stream term stream
     * @return string arguemnts
     */
    @Nonnull
    private static String values( @Nonnull final Stream<ITerm> p_stream )
    {
        return p_stream.filter( i -> i instanceof ILiteral )
                       .map( ITerm::<ILiteral>raw )
                       .collect( Collectors.toMap( ITerm::functor, i -> valueformat( i.raw() ), ( i, j ) -> i ) )
                       .values()
                       .stream()
                       .collect( Collectors.joining( " " ) )
                       .trim();
    }

    /**
     * creates the field list
     *
     * @param p_stream field stream
     * @return string definition
     */
    @Nonnull
    private static String fields( @Nonnull final Stream<ITerm> p_stream )
    {
        return p_stream.filter( i -> i instanceof ILiteral )
                       .map( ITerm::<ILiteral>raw )
                       .filter( i -> i.values().noneMatch( j -> j instanceof IRawTerm ) )
                       .collect( Collectors.toMap( ITerm::functor, CQueryLiteral::root, ( i, j ) -> i ) )
                       .values()
                       .stream()
                       .collect( Collectors.joining( " " ) )
                       .trim();
    }

    /**
     * format query argument
     * @param p_literal literal
     * @return functor with value
     */
    @Nonnull
    private static String valueformat( @Nonnull final ILiteral p_literal )
    {
        return p_literal.values()
                        .filter( i -> i instanceof IRawTerm<?> )
                        .findFirst()
                        .map( ITerm::raw )
                        .map( CQueryLiteral::typeformat )
                        .map( i -> p_literal.functor() + " : " + i )
                        .orElse( "" );
    }

    /**
     * type mapping
     *
     * @param p_value any value
     * @return graphql string
     */
    private static String typeformat( final Object p_value )
    {
        if ( p_value instanceof String )
            return MessageFormat.format( "\"{0}\"", p_value );

        if ( p_value instanceof Collection<?> )
            return MessageFormat.format(
                "[{0}]",
                ( (Collection<?>) p_value ).stream().map( CQueryLiteral::typeformat ).collect( Collectors.joining( ", " ) )
            );

        return p_value.toString();
    }

}