/*******************************************************************************
* Copyright 2014 Analog Devices, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************/
package com.analog.lyric.dimple.events;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.collect.BitSetUtil;
import com.analog.lyric.dimple.model.core.FactorGraph;
import com.analog.lyric.dimple.options.DimpleOptionHolder;
import com.analog.lyric.dimple.solvers.interfaces.ISolverFactorGraph;
import com.analog.lyric.dimple.solvers.interfaces.ISolverFactorGraphChild;
import com.analog.lyric.util.misc.Internal;
/**
* Abstract base implementation of {@link ISolverEventSource}
* @since 0.06
* @author Christopher Barber
*/
public abstract class SolverEventSource
extends DimpleOptionHolder
implements ISolverEventSource, ISolverFactorGraphChild
{
/*-----------
* Constants
*/
/**
* Bits in {@link #_flags} reserved by this class.
*/
protected static final int RESERVED_FLAGS = 0xF0000000;
/*-------
* State
*/
/**
* Temporary flags that can be used to mark the node during the execution of various algorithms
* or to mark non-static attributes of the node.
* <p>
* The flags are automatically cleared by {@link #initialize()}.
* <p>
* Subclasses should document which flags are reserved for use by that class and which ones
* are available for use by subclasses.
* <p>
* Flags should generally be get/set using method provided by this class.
* <p>
* @since 0.06
* @see #clearFlags()
* @see #clearFlags(int)
* @see #isFlagSet(int)
* @see #setFlags(int)
*/
protected int _flags;
/*----------------------------
* IDimpleEventSource methods
*/
@Override
public @Nullable FactorGraph getContainingGraph()
{
return getModelObject().getContainingGraph();
}
@Override
public @Nullable ISolverFactorGraph getEventParent()
{
return getParentGraph();
}
/**
* {@inheritDoc}
* <p>
* Default implementation provided by this class returns the event source name
* of {@link #getModelEventSource()} if non-null, otherwise the result of the {@link #toString()} method.
* <p>
* This behavior is likely to change in the future.
*/
@Override
public String getEventSourceName()
{
IModelEventSource modelObj = getModelEventSource();
return modelObj != null ? modelObj.getEventSourceName() : toString();
}
/*----------------------------
* ISolverEventSource methods
*/
@Override
public void notifyListenerChanged()
{
clearFlags(getEventMask());
}
/*----------------------------
* Protected/internal methods
*/
/**
* Clear all flag values. Invoked automatically by {@link #initialize()}.
*/
protected final void clearFlags()
{
_flags = 0;
}
/**
* Clear flags in given mask.
*/
protected final void clearFlags(int mask)
{
_flags = BitSetUtil.clearMask(_flags, mask);
}
/**
* Return mask of flag bits that are used to determine whether to
* generate events. This is used by {@link #notifyListenerChanged()}
* to clear the specified flag bits. It is assumed that the value of
* all zeros indicates that the object needs to recompute its flags
* based on the listener.
* <p>
* The default implementation returns zero.
* <p>
* Overriders should usually combine local value with value from super class using bitwise or, e.g.:
* <pre>
* return ThisClass.EVENT_MASK | super.getEventMask();
* </pre>
*
* @since 0.06
*/
protected int getEventMask()
{
return 0;
}
@Internal
public final int getFlagValue(int mask)
{
return BitSetUtil.getMaskedValue(_flags, mask);
}
/**
* True if all of the bits in {@code mask} are set in the flags.
*/
protected final boolean isFlagSet(int mask)
{
return BitSetUtil.isMaskSet(_flags, mask);
}
protected final void raiseEvent(@Nullable SolverEvent event)
{
if (event != null)
{
final IDimpleEventListener listener = getEventListener();
final boolean handled = listener != null && listener.raiseEvent(event);
if (!handled)
{
// Listener configuration probably changed. Reconfigure source to
// prevent further event creation.
notifyListenerChanged();
}
}
}
@Internal
protected ISolverFactorGraph requireParentGraph()
{
final ISolverFactorGraph parent = getParentGraph();
if (parent == null)
{
throw new IllegalStateException(String.format("'%s' does not belong to any solver graph.", this));
}
return parent;
}
/**
* Sets all of the bits in {@code mask} in the flags.
*/
protected final void setFlags(int mask)
{
_flags = BitSetUtil.setMask(_flags, mask);
}
/**
* Sets bits of flag specified by {@code mask} to {@code value}.
*/
@Internal
public final void setFlagValue(int mask, int value)
{
_flags = BitSetUtil.setMaskedValue(_flags, mask, value);
}
}