CClassStorage.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.beliefbase.storage;

import org.lightjason.agentspeak.language.CLiteral;
import org.lightjason.agentspeak.language.CRawTerm;
import org.lightjason.agentspeak.language.ILiteral;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * belief storage to get access to all class attributes
 *
 * @note all object attributes which are not transient will be read
 * @todo implement recursive descent of properties
 * @todo implement renaming function of properties
 */
public final class CClassStorage<M> extends IBaseStorage<ILiteral, M>
{
    /**
     * object instance
     */
    private final Object m_instance;
    /**
     * map with functor and field reference
     */
    private final Map<String, Field> m_fields;

    /**
     * ctor
     *
     * @param p_instance object
     */
    public CClassStorage( @Nonnull final Object p_instance )
    {
        this( p_instance, i -> i );
    }

    /**
     * ctor
     *
     * @param p_instance object
     * @param p_fieldnameformater function to reformat field names
     */
    public CClassStorage( @Nonnull final Object p_instance, @Nonnull final Function<String, String> p_fieldnameformater )
    {
        m_instance = p_instance;
        m_fields = Collections.unmodifiableMap(
            Arrays.stream( m_instance.getClass().getDeclaredFields() )
                  .peek( i -> i.setAccessible( true ) )
                  .filter( i -> !Modifier.isTransient( i.getModifiers() ) )
                  .filter( i -> !Modifier.isStatic( i.getModifiers() ) )
                  .collect( Collectors.toMap(
                      i -> p_fieldnameformater.apply( i.getName() ).toLowerCase( Locale.ROOT ).replace( "\\s+", "" ), i -> i )
                  )
        );
    }

    @Nonnull
    @Override
    public final Stream<ILiteral> streamMultiElements()
    {
        return m_fields.entrySet().stream()
                       .map( i -> this.literal( i.getKey(), i.getValue() ) )
                       .filter( Objects::nonNull );
    }

    @Nonnull
    @Override
    public final Stream<M> streamSingleElements()
    {
        return Stream.empty();
    }

    @Override
    public final  boolean containsMultiElement( @Nonnull final String p_key )
    {
        return m_fields.containsKey( p_key );
    }

    @Override
    public final boolean containsSingleElement( @Nonnull final String p_key )
    {
        return false;
    }

    @Override
    public final boolean putMultiElement( @Nonnull final String p_key, final ILiteral p_value )
    {
        final Field l_field = m_fields.get( p_key );
        if ( ( Objects.isNull( l_field ) ) || ( p_value.emptyValues() ) || ( Modifier.isFinal( l_field.getModifiers() ) ) )
            return false;

        try
        {
            l_field.set( m_instance, p_value.values().findFirst().orElseGet( () -> CRawTerm.from( null ) ).raw() );
            return true;
        }
        catch ( final IllegalAccessException l_exception )
        {
            return false;
        }
    }

    @Override
    public final boolean putSingleElement( @Nonnull final String p_key, final M p_value )
    {
        return false;
    }

    @Override
    public final boolean putSingleElementIfAbsent( @Nonnull final String p_key, final M p_value )
    {
        return false;
    }

    @Override
    public final boolean removeMultiElement( @Nonnull final String p_key, final ILiteral p_value )
    {
        return false;
    }

    @Override
    public final boolean removeSingleElement( @Nonnull final String p_key )
    {
        return false;
    }

    @Override
    public final M getSingleElement( @Nonnull final String p_key )
    {
        return null;
    }

    @Override
    public final M getSingleElementOrDefault( @Nonnull final String p_key, final M p_default )
    {
        return p_default;
    }

    @Nonnull
    @Override
    public Collection<ILiteral> getMultiElement( @Nonnull final String p_key )
    {
        final Field l_field = m_fields.get( p_key );
        return Objects.isNull( l_field ) ? Collections.emptySet() : Stream.of( this.literal( p_key, l_field ) ).collect( Collectors.toSet() );
    }

    @Override
    public final void clear()
    {
    }

    @Override
    public final boolean empty()
    {
        return m_fields.isEmpty();
    }

    @Override
    public final int size()
    {
        return m_fields.size();
    }

    /**
     * returns a literal definition of the a class field
     *
     * @param p_name literal functor
     * @param p_field field reference
     * @return null or literal
     */
    @Nullable
    private ILiteral literal( @Nonnull final String p_name, @Nonnull final Field p_field )
    {
        try
        {
            final Object l_value = p_field.get( m_instance );
            return Objects.isNull( l_value ) ? CLiteral.from( p_name, CRawTerm.EMPTY ) : CLiteral.from( p_name, CRawTerm.from( l_value ) );
        }
        catch ( final IllegalAccessException l_exception )
        {
            return null;
        }
    }

}