/******************************************************************************* * 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); } }