CASTVisitorType.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.grammar;

import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
import org.lightjason.agentspeak.action.IAction;
import org.lightjason.agentspeak.common.CPath;
import org.lightjason.agentspeak.common.IPath;
import org.lightjason.agentspeak.error.CIllegalArgumentException;
import org.lightjason.agentspeak.error.CSyntaxErrorException;
import org.lightjason.agentspeak.language.CLiteral;
import org.lightjason.agentspeak.language.CRawTerm;
import org.lightjason.agentspeak.language.ILiteral;
import org.lightjason.agentspeak.language.ITerm;
import org.lightjason.agentspeak.language.execution.IExecution;
import org.lightjason.agentspeak.language.execution.action.CProxyAction;
import org.lightjason.agentspeak.language.execution.action.CRawAction;
import org.lightjason.agentspeak.language.execution.action.CTernaryOperation;
import org.lightjason.agentspeak.language.execution.action.achievement_test.CAchievementRuleLiteral;
import org.lightjason.agentspeak.language.execution.action.unify.CDefaultUnify;
import org.lightjason.agentspeak.language.execution.action.unify.CExpressionUnify;
import org.lightjason.agentspeak.language.execution.action.unify.CVariableUnify;
import org.lightjason.agentspeak.language.execution.expression.CAtom;
import org.lightjason.agentspeak.language.execution.expression.CProxyReturnExpression;
import org.lightjason.agentspeak.language.execution.expression.EOperator;
import org.lightjason.agentspeak.language.execution.expression.IExpression;
import org.lightjason.agentspeak.language.execution.expression.logical.CUnary;
import org.lightjason.agentspeak.language.execution.expression.numerical.CAdditive;
import org.lightjason.agentspeak.language.execution.expression.numerical.CComparable;
import org.lightjason.agentspeak.language.execution.expression.numerical.CMultiplicative;
import org.lightjason.agentspeak.language.execution.expression.numerical.CPower;
import org.lightjason.agentspeak.language.execution.expression.numerical.CRelational;
import org.lightjason.agentspeak.language.instantiable.rule.IRule;
import org.lightjason.agentspeak.language.variable.CMutexVariable;
import org.lightjason.agentspeak.language.variable.CVariable;
import org.lightjason.agentspeak.language.variable.IVariable;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * parser for complex-datatypes
 */
@SuppressWarnings( {"all", "warnings", "unchecked", "unused", "cast"} )
public final class CASTVisitorType extends AbstractParseTreeVisitor<Object> implements IASTVisitorType
{
    /**
     * map with action definition
     */
    private final Map<IPath, IAction> m_actions;
    /**
     * map with logical rules
     */
    private final Multimap<IPath, IRule> m_rules = LinkedHashMultimap.create();
    /**
     * parsed literal
     */
    private ILiteral m_literal = ILiteral.EMPTY;
    /**
     * parsed expression
     */
    private IExpression m_expression = IExpression.EMPTY;
    /**
     * parsed term
     */
    private ITerm m_term = ITerm.EMPTY;


    /**
     * ctor
     */
    public CASTVisitorType()
    {
        this( Collections.<IAction>emptySet(), Collections.<IRule>emptySet() );
    }

    /**
     * ctor
     *
     * @param p_actions set with actions
     * @param p_rules set with rules
     */
    public CASTVisitorType( @Nonnull final Set<IAction> p_actions, @Nonnull final Set<IRule> p_rules )
    {
        m_actions = p_actions.stream().collect( Collectors.toMap( i -> i.name(), i -> i ) );
        p_rules.stream().forEach( i -> m_rules.put( i.identifier().fqnfunctor(), i ) );
    }

    // --- start rules -----------------------------------------------------------------------------------------------------------------------------------------


    @Override
    public final Object visitLiteral_type( final TypeParser.Literal_typeContext p_context )
    {
        m_literal = (ILiteral) this.visitChildren( p_context );
        return null;
    }

    @Override
    public final Object visitExpression_type( final TypeParser.Expression_typeContext p_context )
    {
        m_expression = (IExpression) this.visitChildren( p_context );
        return null;
    }

    @Override
    public final Object visitExpression_term( final TypeParser.Expression_termContext p_context )
    {
        m_term = (ITerm) this.visitChildren( p_context );
        return null;
    }

    @Override
    public final Object visitExecutable_term( final TypeParser.Executable_termContext p_context )
    {
        if ( Objects.nonNull( p_context.STRING() ) )
            return new CRawAction<>( p_context.STRING() );
        if ( Objects.nonNull( p_context.number() ) )
            return new CRawAction<>( this.visitNumber( p_context.number() ) );
        if ( Objects.nonNull( p_context.LOGICALVALUE() ) )
            return new CRawAction<>( logicalvalue( p_context.LOGICALVALUE().getText() ) );

        if ( Objects.nonNull( p_context.executable_action() ) )
            return this.visitExecutable_action( p_context.executable_action() );
        if ( Objects.nonNull( p_context.executable_rule() ) )
            return this.visitExecutable_rule( p_context.executable_rule() );

        if ( Objects.nonNull( p_context.expression() ) )
            return this.visitExpression( p_context.expression() );
        if ( Objects.nonNull( p_context.ternary_operation() ) )
            return this.visitTernary_operation( p_context.ternary_operation() );

        throw new CIllegalArgumentException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "termunknown", p_context.getText() ) );
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------


    // --- term element rules ----------------------------------------------------------------------------------------------------------------------------------

    @Override
    public final Object visitUnification( final TypeParser.UnificationContext p_context )
    {
        final Object l_constraint = this.visitUnification_constraint( p_context.unification_constraint() );

        if ( l_constraint instanceof IExpression )
            return new CExpressionUnify(
                Objects.nonNull( p_context.AT() ),
                (ILiteral) this.visitLiteral( p_context.literal() ),
                (IExpression) l_constraint
            );

        if ( l_constraint instanceof IVariable<?> )
            return new CVariableUnify(
                Objects.nonNull( p_context.AT() ),
                (ILiteral) this.visitLiteral( p_context.literal() ),
                (IVariable<?>) l_constraint
            );

        return new CDefaultUnify( Objects.nonNull( p_context.AT() ), (ILiteral) this.visitLiteral( p_context.literal() ) );
    }

    @Override
    public final Object visitUnification_constraint( final TypeParser.Unification_constraintContext p_context )
    {
        if ( Objects.isNull( p_context ) )
            return null;

        if ( Objects.nonNull( p_context.expression() ) )
            return this.visitExpression( p_context.expression() );

        if ( Objects.nonNull( p_context.variable() ) )
            return this.visitVariable( p_context.variable() );

        return null;
    }

    @Override
    public final Object visitTernary_operation( final TypeParser.Ternary_operationContext p_context )
    {
        return new CTernaryOperation(
            (IExpression) this.visitExpression( p_context.expression() ),
            (IExecution) this.visitTernary_operation_true( p_context.ternary_operation_true() ),
            (IExecution) this.visitTernary_operation_false( p_context.ternary_operation_false() )
        );
    }

    @Override
    public final Object visitTernary_operation_true( final TypeParser.Ternary_operation_trueContext p_context )
    {
        return this.visitExecutable_term( p_context.executable_term() );
    }

    @Override
    public final Object visitTernary_operation_false( final TypeParser.Ternary_operation_falseContext p_context )
    {
        return this.visitExecutable_term( p_context.executable_term() );
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------


    // --- simple datatypes ------------------------------------------------------------------------------------------------------------------------------------

    @Override
    public final Object visitLiteral( final TypeParser.LiteralContext p_context )
    {
        return new CLiteral(
            Objects.nonNull( p_context.AT() ),
            Objects.nonNull( p_context.STRONGNEGATION() ),
            CPath.from( this.visitAtom( p_context.atom() ).toString() ),
            (Collection<ITerm>) this.visitTermlist( p_context.termlist() )
        );
    }

    @Override
    public final Object visitTerm( final TypeParser.TermContext p_context )
    {
        if ( Objects.nonNull( p_context.STRING() ) )
            return stringvalue( p_context.STRING().getText() );
        if ( Objects.nonNull( p_context.number() ) )
            return this.visitNumber( p_context.number() );
        if ( Objects.nonNull( p_context.LOGICALVALUE() ) )
            return logicalvalue( p_context.LOGICALVALUE().getText() );

        if ( Objects.nonNull( p_context.literal() ) )
            return this.visitLiteral( p_context.literal() );
        if ( Objects.nonNull( p_context.variable() ) )
            return this.visitVariable( p_context.variable() );

        if ( Objects.nonNull( p_context.termlist() ) )
            return this.visitTermlist( p_context.termlist() );
        if ( Objects.nonNull( p_context.expression() ) )
            return this.visitExpression( p_context.expression() );
        if ( Objects.nonNull( p_context.ternary_operation() ) )
            return this.visitTernary_operation( p_context.ternary_operation() );

        throw new CIllegalArgumentException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "termunknown", p_context.getText() ) );
    }

    @Override
    public final Object visitTermlist( final TypeParser.TermlistContext p_context )
    {
        if ( ( Objects.isNull( p_context ) ) || ( p_context.isEmpty() ) )
            return Collections.<ITerm>emptyList();

        return p_context.term().stream()
                        .map( i -> this.visitTerm( i ) )
                        .filter( i -> Objects.nonNull( i ) )
                        .map( i -> i instanceof ITerm ? (ITerm) i : CRawTerm.from( i ) )
                        .collect( Collectors.toList() );
    }

    @Override
    public final Object visitVariablelist( final TypeParser.VariablelistContext p_context )
    {
        return this.visitChildren( p_context );
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------


    // --- raw rules -------------------------------------------------------------------------------------------------------------------------------------------

    @Override
    public final Object visitNumber( final TypeParser.NumberContext p_context )
    {
        if ( Objects.nonNull( p_context.CONSTANTNUMBER() ) )
            return numericonstant( p_context.CONSTANTNUMBER().getText() );

        final Number l_value = (Number) this.visitChildren( p_context );
        return Objects.nonNull( p_context.MINUS() )
               ? -1 * l_value.doubleValue()
               : l_value.doubleValue();
    }

    @Override
    public final Object visitDigitsequence( final TypeParser.DigitsequenceContext p_context )
    {
        return Double.valueOf( p_context.getText() );
    }

    @Override
    public final Object visitAtom( final TypeParser.AtomContext p_context )
    {
        return p_context.getText();
    }

    @Override
    public final Object visitVariable( final TypeParser.VariableContext p_context )
    {
        return Objects.isNull( p_context.AT() )
               ? new CVariable<>( (String) this.visitVariableatom( p_context.variableatom() ) )
               : new CMutexVariable<>( (String) this.visitVariableatom( p_context.variableatom() ) );
    }

    @Override
    public final Object visitVariableatom( final TypeParser.VariableatomContext p_context )
    {
        return p_context.getText();
    }

    @Override
    public final Object visitExpression( final TypeParser.ExpressionContext p_context )
    {
        // bracket expression
        if ( Objects.nonNull( p_context.expression_bracket() ) )
            return this.visitExpression_bracket( p_context.expression_bracket() );

        // or-expression
        return CCommon.createLogicalBinaryExpression(
            EOperator.OR,
            (IExpression) this.visitExpression_logical_and( p_context.expression_logical_and() ),
            Objects.nonNull( p_context.expression() )
            ? p_context.expression().stream().map( i -> (IExpression) this.visitExpression( i ) ).collect(
                Collectors.toList() )
            : Collections.<IExpression>emptyList()
        );
    }

    @Override
    public final Object visitExpression_bracket( final TypeParser.Expression_bracketContext p_context )
    {
        return this.visitExpression( p_context.expression() );
    }

    @Override
    public final Object visitExpression_logical_and( final TypeParser.Expression_logical_andContext p_context )
    {
        return CCommon.createLogicalBinaryExpression(
            EOperator.AND,
            (IExpression) this.visitExpression_logical_xor( p_context.expression_logical_xor() ),
            Objects.nonNull( p_context.expression() )
            ? p_context.expression().stream().map( i -> (IExpression) this.visitExpression( i ) ).collect(
                Collectors.toList() )
            : Collections.<IExpression>emptyList()
        );
    }

    @Override
    public final Object visitExpression_logical_xor( final TypeParser.Expression_logical_xorContext p_context )
    {
        if ( Objects.nonNull( p_context.expression_logical_element() ) )
            return CCommon.createLogicalBinaryExpression(
                EOperator.XOR,
                (IExpression) this.visitExpression_logical_element( p_context.expression_logical_element() ),
                Objects.nonNull( p_context.expression() )
                ? p_context.expression().stream().map( i -> (IExpression) this.visitExpression( i ) ).collect(
                    Collectors.toList() )
                : Collections.<IExpression>emptyList()
            );

        if ( Objects.nonNull( p_context.expression_logical_negation() ) )
            return this.visitExpression_logical_negation( p_context.expression_logical_negation() );

        if ( Objects.nonNull( p_context.expression_numeric() ) )
            return this.visitExpression_numeric( p_context.expression_numeric() );

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "logicallefthandside", p_context.getText() ) );
    }

    @Override
    public final Object visitExpression_logical_negation( final TypeParser.Expression_logical_negationContext p_context )
    {
        return new CUnary( EOperator.NEGATION, (IExpression) this.visitExpression( p_context.expression() ) );
    }

    @Override
    public final Object visitExpression_logical_element( final TypeParser.Expression_logical_elementContext p_context )
    {
        if ( Objects.nonNull( p_context.LOGICALVALUE() ) )
            return new CAtom( logicalvalue( p_context.LOGICALVALUE().getText() ) );

        if ( Objects.nonNull( p_context.variable() ) )
            return new CAtom( this.visitVariable( p_context.variable() ) );

        if ( Objects.nonNull( p_context.unification() ) )
            return new CProxyReturnExpression<>( (IExecution) this.visitUnification( p_context.unification() ) );

        if ( Objects.nonNull( p_context.executable_action() ) )
            return new CProxyReturnExpression<>( (IExecution) this.visitExecutable_action( p_context.executable_action() ) );

        if ( Objects.nonNull( p_context.executable_rule() ) )
            return new CProxyReturnExpression<>( (IExecution) this.visitExecutable_rule( p_context.executable_rule() ) );

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "logicalelement", p_context.getText() ) );
    }

    @Override
    public final Object visitExpression_numeric( final TypeParser.Expression_numericContext p_context )
    {
        if ( Objects.isNull( p_context.expression_numeric() ) )
            return this.visitExpression_numeric_relation( p_context.expression_numeric_relation() );

        if ( Objects.nonNull( p_context.EQUAL() ) )
            return new CComparable(
                EOperator.EQUAL,
                (IExpression) this.visitExpression_numeric_relation( p_context.expression_numeric_relation() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        if ( Objects.nonNull( p_context.NOTEQUAL() ) )
            return new CComparable(
                EOperator.NOTEQUAL,
                (IExpression) this.visitExpression_numeric_relation( p_context.expression_numeric_relation() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "compareoperator", p_context.getText() ) );
    }

    @Override
    public final Object visitExpression_numeric_relation( final TypeParser.Expression_numeric_relationContext p_context )
    {
        if ( Objects.isNull( p_context.expression_numeric() ) )
            return this.visitExpression_numeric_additive( p_context.expression_numeric_additive() );

        if ( Objects.nonNull( p_context.GREATER() ) )
            return new CRelational(
                EOperator.GREATER,
                (IExpression) this.visitExpression_numeric_additive( p_context.expression_numeric_additive() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        if ( Objects.nonNull( p_context.GREATEREQUAL() ) )
            return new CRelational(
                EOperator.GREATEREQUAL,
                (IExpression) this.visitExpression_numeric_additive( p_context.expression_numeric_additive() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        if ( Objects.nonNull( p_context.LESS() ) )
            return new CRelational(
                EOperator.LESS,
                (IExpression) this.visitExpression_numeric_additive( p_context.expression_numeric_additive() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        if ( Objects.nonNull( p_context.LESSEQUAL() ) )
            return new CRelational(
                EOperator.LESSEQUAL,
                (IExpression) this.visitExpression_numeric_additive( p_context.expression_numeric_additive() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "relationaloperator", p_context.getText() ) );
    }

    @Override
    public final Object visitExpression_numeric_additive( final TypeParser.Expression_numeric_additiveContext p_context )
    {
        if ( Objects.isNull( p_context.expression_numeric() ) )
            return this.visitExpression_numeric_multiplicative( p_context.expression_numeric_multiplicative() );

        if ( Objects.nonNull( p_context.PLUS() ) )
            return new CAdditive(
                EOperator.PLUS,
                (IExpression) this.visitExpression_numeric_multiplicative( p_context.expression_numeric_multiplicative() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        if ( Objects.nonNull( p_context.MINUS() ) )
            return new CAdditive(
                EOperator.MINUS,
                (IExpression) this.visitExpression_numeric_multiplicative( p_context.expression_numeric_multiplicative() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "additiveoperator", p_context.getText() ) );
    }

    @Override
    public final Object visitExpression_numeric_multiplicative( final TypeParser.Expression_numeric_multiplicativeContext p_context )
    {
        if ( Objects.isNull( p_context.expression_numeric() ) )
            return this.visitExpression_numeric_power( p_context.expression_numeric_power() );

        if ( Objects.nonNull( p_context.MULTIPLY() ) )
            return new CMultiplicative(
                EOperator.MULTIPLY,
                (IExpression) this.visitExpression_numeric_power( p_context.expression_numeric_power() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        if ( Objects.nonNull( p_context.SLASH() ) )
            return new CMultiplicative(
                EOperator.DIVIDE,
                (IExpression) this.visitExpression_numeric_power( p_context.expression_numeric_power() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        if ( Objects.nonNull( p_context.MODULO() ) )
            return new CMultiplicative(
                EOperator.MODULO,
                (IExpression) this.visitExpression_numeric_power( p_context.expression_numeric_power() ),
                (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
            );

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "multiplicativeoperator", p_context.getText() ) );
    }

    @Override
    public final Object visitExpression_numeric_power( final TypeParser.Expression_numeric_powerContext p_context )
    {
        if ( Objects.isNull( p_context.expression_numeric() ) )
            return this.visitExpression_numeric_element( p_context.expression_numeric_element() );

        return new CPower(
            EOperator.POWER,
            (IExpression) this.visitExpression_numeric_element( p_context.expression_numeric_element() ),
            (IExpression) this.visitExpression_numeric( p_context.expression_numeric() )
        );
    }

    @Override
    public final Object visitExpression_numeric_element( final TypeParser.Expression_numeric_elementContext p_context )
    {
        if ( Objects.nonNull( p_context.number() ) )
            return new CAtom( this.visitNumber( p_context.number() ) );

        if ( Objects.nonNull( p_context.variable() ) )
            return new CAtom( this.visitVariable( p_context.variable() ) );

        if ( Objects.nonNull( p_context.executable_action() ) )
            return new CProxyReturnExpression<>( (IExecution) this.visitExecutable_action( p_context.executable_action() ) );

        if ( Objects.nonNull( p_context.executable_rule() ) )
            return new CProxyReturnExpression<>( (IExecution) this.visitExecutable_rule( p_context.executable_rule() ) );

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "numericelement", p_context.getText() ) );
    }

    @Override
    public final Object visitExecutable_action( final TypeParser.Executable_actionContext p_context )
    {
        return new CProxyAction( m_actions, (ILiteral) this.visitLiteral( p_context.literal() ) );
    }

    @Override
    public final Object visitExecutable_rule( final TypeParser.Executable_ruleContext p_context )
    {
        if ( Objects.nonNull( p_context.literal() ) )
            return new CAchievementRuleLiteral( (ILiteral) this.visitLiteral( p_context.literal() ) );

        return null;
    }

    @Override
    public final Object visitVariable_evaluate( final TypeParser.Variable_evaluateContext p_context )
    {
        return null;
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------


    // --- helper ----------------------------------------------------------------------------------------------------------------------------------------------

    /**
     * returns the value of a numeric constant
     *
     * @param p_value constant name
     * @return number value
     */
    private static Number numericonstant( @Nonnull final String p_value )
    {
        final Double l_constant = org.lightjason.agentspeak.grammar.CCommon.NUMERICCONSTANT.get( p_value );
        if ( Objects.nonNull( l_constant ) )
            return l_constant;

        throw new CSyntaxErrorException( org.lightjason.agentspeak.common.CCommon.languagestring( CASTVisitorType.class, "constantunknown", p_value ) );
    }

    /**
     * converts a string token to the type
     *
     * @param p_value string value
     * @return boolean value
     */
    private static boolean logicalvalue( @Nonnull final String p_value )
    {
        return ( !p_value.isEmpty() ) && ( ( "true".equals( p_value ) ) || ( "success".equals( p_value ) ) );
    }

    /**
     * create a string value without quotes
     *
     * @param p_value string
     * @return string without quotes
     */
    private static String stringvalue( @Nonnull final String p_value )
    {
        return p_value.length() < 3 ? "" : p_value.substring( 1, p_value.length() - 1 );
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------


    // --- getter structure ------------------------------------------------------------------------------------------------------------------------------------

    @Nonnull
    @Override
    public final ILiteral literal()
    {
        return m_literal;
    }

    @Nonnull
    @Override
    public final IExpression expression()
    {
        return m_expression;
    }

    @Nonnull
    @Override
    public final ITerm term()
    {
        return m_term;
    }

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------

}