IBaseBeliefbase.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;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.lightjason.agentspeak.agent.IAgent;
import org.lightjason.agentspeak.beliefbase.view.CView;
import org.lightjason.agentspeak.beliefbase.view.IView;
import org.lightjason.agentspeak.language.ILiteral;
import org.lightjason.agentspeak.language.instantiable.plan.trigger.CTrigger;
import org.lightjason.agentspeak.language.instantiable.plan.trigger.ITrigger;

import javax.annotation.Nonnull;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;


/**
 * default behaviour of a beliefbase
 *
 * @tparam T agent type
 * @todo check reference counting on delete views
 * @see http://docs.oracle.com/javase/8/docs/api/java/lang/ref/PhantomReference.html
 * @see http://docs.oracle.com/javase/8/docs/api/java/lang/ref/WeakReference.html
 * @see https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references
 */
@SuppressFBWarnings( "RI_REDUNDANT_INTERFACES" )
public abstract class IBaseBeliefbase implements IBeliefbase
{

    /**
     * map with events for a mask
     */
    private final Multimap<IView, ITrigger> m_events = Multimaps.synchronizedSetMultimap( HashMultimap.create() );
    /**
     * set with all current views
     */
    private final Set<IView> m_views = Collections.synchronizedSet( new HashSet<>() );
    /**
     * weak reference queue of all masks to avoid memory-leaks of belief events
     */
    private final ReferenceQueue<IView> m_maskreference = new ReferenceQueue<>();


    @Nonnull
    @Override
    public final IView create( @Nonnull final String p_name )
    {
        return this.eventreference( new CView( p_name, this ) );
    }

    @Nonnull
    @Override
    public final IView create( @Nonnull final String p_name, final IView p_parent )
    {
        return this.eventreference( new CView( p_name, this, p_parent ) );
    }

    @Nonnull
    @Override
    public ILiteral add( @Nonnull final ILiteral p_literal )
    {
        return this.event( ITrigger.EType.ADDBELIEF, p_literal );
    }

    @Nonnull
    @Override
    public ILiteral remove( @Nonnull final ILiteral p_literal )
    {
        return this.event( ITrigger.EType.DELETEBELIEF, p_literal );
    }

    @Nonnull
    @Override
    public IAgent<?> update( @Nonnull final IAgent<?> p_agent )
    {
        // check all references of mask and remove unused references
        for ( Reference<? extends IView> l_reference = m_maskreference.poll(); Objects.nonNull( l_reference ); l_reference = m_maskreference.poll() )
        {
            final IView l_view = l_reference.get();
            if ( Objects.nonNull( l_view ) )
            {
                m_views.remove( l_view );
                m_events.asMap().remove( l_view );
            }
        }

        return p_agent;
    }

    @Nonnull
    @Override
    public Stream<ITrigger> trigger( @Nonnull final IView p_view )
    {
        return this.cleartrigger( p_view );
    }


    /**
     * push an event and literal to the event map
     *
     * @param p_event event
     * @param p_literal literal
     */
    protected ILiteral event( final ITrigger.EType p_event, final ILiteral p_literal )
    {
        final ITrigger l_trigger = CTrigger.from( p_event, p_literal );
        m_views.parallelStream().forEach( i -> m_events.put( i, l_trigger ) );
        return p_literal;
    }

    /**
     * removes the interal view references
     *
     * @param p_view view to remove
     * @return view
     */
    protected final IView internalremove( final IView p_view )
    {
        m_views.remove( p_view );
        m_events.removeAll( p_view );
        return p_view;
    }

    /**
     * adds a view to the event referencing structure
     *
     * @param p_view view
     * @return input view
     */
    protected IView eventreference( final IView p_view )
    {
        new PhantomReference<>( p_view, m_maskreference );
        m_views.add( p_view );
        return p_view;
    }

    /**
     * copy of all trigger values
     *
     * @param p_view trigger of this view
     * @return set with trigger values
     */
    protected final Stream<ITrigger> cleartrigger( final IView p_view )
    {
        return m_events.removeAll( p_view ).stream();
    }

}