/*******************************************************************************
* 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.options;
import java.util.Arrays;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.collect.ArrayUtil;
import com.analog.lyric.dimple.environment.DimpleEnvironment;
import com.analog.lyric.dimple.environment.IDimpleEnvironmentHolder;
import com.analog.lyric.dimple.events.EventSourceIterator;
import com.analog.lyric.dimple.events.IDimpleEventListener;
import com.analog.lyric.dimple.events.IDimpleEventSource;
import com.analog.lyric.dimple.model.core.FactorGraph;
import com.analog.lyric.options.IOptionHolder;
import com.analog.lyric.options.IOptionKey;
import com.analog.lyric.options.LocalOptionHolder;
import com.analog.lyric.options.OptionDoubleList;
/**
* Base class for dimple objects that can hold options and generate events.
* <p>
* This extends {@link LocalOptionHolder} in the following ways:
* <ul>
* <li>It also implements the {@link IDimpleEventSource} interface, so that all Dimple objects
* on which you can set options are also event sources.
* <li>It overrides the {@link #getOptionDelegates()} method
* </ul>
*
* @since 0.07
* @author Christopher Barber
*/
public abstract class DimpleOptionHolder
extends LocalOptionHolder
implements IDimpleEventSource, IDimpleEnvironmentHolder
{
/*--------------
* Construction
*/
protected DimpleOptionHolder()
{
}
protected DimpleOptionHolder(DimpleOptionHolder other)
{
super(other);
}
/*-----------------------
* IOptionHolder methods
*/
/**
* Iterates over option holders in the order in which options should be looked up.
* <p>
* Unlike the default implementation, which simply walks up the chain of {@linkplain #getOptionParent option
* parents}, this will visit both the option parent and the corresponding model object. This is described in
* more detail in {@link EventSourceIterator}.
*/
@Override
public EventSourceIterator getOptionDelegates()
{
return EventSourceIterator.create(this);
}
/**
* {@inheritDoc}
* <p>
* By default, the option parent is the same as the {@linkplain #getEventParent event parent}.
*/
@Override
public @Nullable IOptionHolder getOptionParent()
{
return getEventParent();
}
/*-----------------------------
* IDimpleEventSource methods
*/
@Override
public @Nullable IDimpleEventListener getEventListener()
{
return getEnvironment().getEventListener();
}
/*----------------------------------
* IDimpleEnvironmentHolder methods
*/
/**
* {@inheritDoc}
* <p>
* Returns environment for {@linkplain #getContainingGraph() containing graph} if available, otherwise
* {@link DimpleEnvironment#active()}.
*/
@Override
public DimpleEnvironment getEnvironment()
{
FactorGraph graph = getContainingGraph();
return graph != null ? graph.getEnvironment() : DimpleEnvironment.active();
}
/*-------------------
* Protected methods
*/
/**
* Returns list of not all zero doubles from option settings.
* <p>
* <ol>
* <li>Look up the option value for {@code listKey} using {@link #getOption}:
* <ul>
* <li>If non-null and contains at least one non-zero value, it's values will be returned.
* <li>If non-null and is empty or contains all zeros, an empty array will be returned.
* </ul>
* <li>Otherwise, look up the option value for {@code singleKey} using {@link #getOptionOrDefault}:
* <ul>
* <li>If zero, an empty array will be returned.
* <li>If non-zero, an array of length {@code size} containing only this value will be returned.
* </ul>
* </ol>
* <p>
* This can be used in situations in which there is a global parameter, such as damping, that
* may be given specific values for different edges of a node.
* <p>
* @param listKey is the primary lookup key for the list.
* @param singleKey is a secondary lookup key to be used if {@code listKey} not set.
* @param size is the size of list to return when replicating value from {@code singleKey}.
* @param array is the array into which the result should be written. This will only be used
* if it is non-null and of exactly the correct length.
* @since 0.07
*/
protected double[] getReplicatedNonZeroListFromOptions(
IOptionKey<OptionDoubleList> listKey,
IOptionKey<Double> singleKey,
int size,
@Nullable double[] array)
{
OptionDoubleList list = getOption(listKey);
if (list != null)
{
final int listSize = list.size();
boolean hasNonZero = false;
for (int i = 0; i < listSize; ++i)
{
if (list.get(i) != 0.0)
{
hasNonZero = true;
break;
}
}
if (!hasNonZero)
{
return ArrayUtil.EMPTY_DOUBLE_ARRAY;
}
if (array == null || array.length != listSize)
{
array = new double[listSize];
}
for (int i = 0; i < listSize; ++i)
{
array[i] = list.get(i);
}
}
else
{
double d = getOptionOrDefault(singleKey);
if (d == 0.0)
{
return ArrayUtil.EMPTY_DOUBLE_ARRAY;
}
if (array == null || array.length != size)
{
array = new double[size];
}
Arrays.fill(array, d);
}
return array;
}
}