/*******************************************************************************
* 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 java.util.concurrent.atomic.AtomicReference;
import net.jcip.annotations.NotThreadSafe;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.collect.UnmodifiableReleasableIterator;
/**
* Iterates over Dimple event sources, which are also Dimple option holders.
* <p>
* This provides the iterator returned by both the
* {@link com.analog.lyric.dimple.options.DimpleOptionHolder#getOptionDelegates DimpleOptionHolder.getOptionDelegates}
* and {@link com.analog.lyric.dimple.events.DimpleEventListener#eventSources DimpleEventListener.eventSources}
* methods. It is used for both option lookup and event dispatching.
* <p>
* The iterator visits objects starting from an initial <em>source</em> using the following recursive ordering:
* <ol>
* <li>Visit <em>source</em>
* <li>Visit <em>source</em>'s corresponding {@linkplain IDimpleEventSource#getModelEventSource model event source}
* if not null and not the same as the <em>source</em> itself. For instance, if <em>source</em> were a Dimple solver
* variable, this step would visit the corresponding model variable.
* <li>If the <em>source</em> has a non-null {@linkplain IDimpleEventSource#getEventParent() parent}, then
* recursively visit's sources starting with the parent.
* <li>Otherwise, if the <em>source</em> has no parent, but visited a <em>model source</em> in step 2 with
* a non-null parent, then the algorithm will recursively visit sources from the model's parent.
* </ol>
* Or written in pseudocode:
* <blockquote>
* <pre>
* void visitSources(IDimpleEventSource source)
* {
* visit(source);
*
* IDimpleEventSource modelSource = source.getModelEventSource();
* boolean hasModel = modelSource != null && modelSource != source;
* if (hasModel)
* visit(modelSource);
*
* IDimpleEventSource parent = source.getEventParent();
* if (parent == null && hasModel)
* parent = model.getEventParent();
*
* if (parent != null)
* {
* visitSources(parent);
* }
* }
* </pre>
* </blockquote>
* <p>
* @since 0.07
* @author Christopher Barber
*/
@NotThreadSafe
public class EventSourceIterator extends UnmodifiableReleasableIterator<IDimpleEventSource>
{
private @Nullable IDimpleEventSource _next;
private @Nullable IDimpleEventSource _prev;
private static final AtomicReference<EventSourceIterator> _reusableInstance = new AtomicReference<>();
/*--------------
* Construction
*/
private EventSourceIterator()
{
}
/**
* Create iterator starting with given source.
* <p>
* Most users should instead use {@link IDimpleEventSource#getOptionDelegates()}.
* <p>
* @param source if null will create an empty iterator.
* @since 0.07
*/
public static EventSourceIterator create(@Nullable IDimpleEventSource source)
{
EventSourceIterator iter = _reusableInstance.getAndSet(null);
if (iter == null)
{
iter = new EventSourceIterator();
}
iter.reset(source);
return iter;
}
/*------------------
* Iterator methods
*/
@Override
public boolean hasNext()
{
return _next != null;
}
@Override
public @Nullable IDimpleEventSource next()
{
IDimpleEventSource source = _next;
if (source != null)
{
// Find next source.
final IDimpleEventSource prev = _prev;
if (prev != null)
{
_next = prev.getEventParent();
_prev = null;
if (_next == null)
{
// If no more parents, use parent of corresponding model object
IModelEventSource modelSource = prev.getModelEventSource();
if (modelSource != prev && modelSource != null)
{
_next = modelSource.getEventParent();
}
}
}
else
{
IModelEventSource modelSource = source.getModelEventSource();
if (modelSource != source)
{
_next = modelSource;
_prev = source;
}
else
{
_next = source.getEventParent();
}
}
}
return source;
}
/*----------------------------
* ReleasableIterator methods
*/
@Override
public void release()
{
reset(null);
_reusableInstance.set(this);
}
/**
* Resets the iterator to start iteration at given {@code source}.
*
* @param source is the first source to visit. If null, the iterator will be empty.
* @since 0.07
*/
public void reset(@Nullable IDimpleEventSource source)
{
_next = source;
_prev = null;
}
}