/*******************************************************************************
* Copyright 2013 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.options;
import java.io.Serializable;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.collect.ReleasableIterator;
/**
* Provides default implementation of some {@link IOptionHolder} methods.
* <p>
* @see LocalOptionHolder
* @see StatelessOptionHolder
* @since 0.07
* @author Christopher Barber
*/
public abstract class AbstractOptionHolder implements IOptionHolder
{
@Override
public ReleasableIterator<? extends IOptionHolder> getOptionDelegates()
{
return OptionParentIterator.create(this);
}
@Override
public @Nullable IOptionHolder getOptionParent()
{
return null;
}
@NonNull // FIXME - workaround for Eclipse JDT bug (467610?)
@Override
public <T extends Serializable> T getOptionOrDefault(IOptionKey<T> key)
{
final T value = getOptionAndSource(key, null);
return value != null ? value : key.defaultValue();
}
@Override
@Nullable
public <T extends Serializable> T getOption(IOptionKey<T> key)
{
return getOptionAndSource(key, null);
}
/*------------------------------
* AbstractOptionHolder methods
*/
/**
* Returns value and source of option with given key if set, else null.
* <p>
* @param key is a non-null option key.
* @param source if non-null with length at least one, the first element will
* be set to the object whose {@linkplain #getLocalOption(IOptionKey) local option}
* setting produced the return value. Nothing will be written if this method returns null.
* @see #getOption
* @since 0.07
*/
@Override
@Nullable
public <T extends Serializable> T getOptionAndSource(IOptionKey<T> key, @Nullable IOptionHolder[] source)
{
T result = getLocalOption(key);
IOptionHolder delegate = this;
if (result == null && !key.local())
{
final ReleasableIterator<? extends IOptionHolder> delegates = getOptionDelegates();
delegates.next(); // skip first delegate, which is this option holder.
while (delegates.hasNext())
{
delegate = delegates.next();
result = delegate.getLocalOption(key);
if (result != null && key.validForDelegator(result, this))
{
break;
}
}
delegates.release();
}
if (result != null && source != null && source.length > 0)
{
source[0] = delegate;
}
return result;
}
/**
* Returns value and source of option with up to specified depth in delegation chain.
* <p>
* @param key is a non-null option key.
* @param maxDepth is the maximum number of {@linkplain #getOptionDelegates option delegates} to examine.
* If zero, then only the current object will be examined. This will be implicitly set to zero if
* {@code key} is {@linkplain IOptionKey#local local}.
* @param depthReference is a non-empty array whose first entry will be set to the depth of the option
* setting in the delegation chain or else set to Integer.MAX_VALUE if no option setting was found.
* @return option value set for given {@code key}. Will be null if option is not set within the first
* 1 + {@code maxDepth} delegates.
* @since 0.07
*/
@Nullable
public <T extends Serializable> T getOptionUptoDepth(IOptionKey<T> key, int maxDepth, int[] depthReference)
{
T result = null;
final ReleasableIterator<? extends IOptionHolder> delegates = getOptionDelegates();
if (key.local())
{
maxDepth = 0;
}
int depth = 0;
for (; depth < maxDepth && delegates.hasNext(); ++depth)
{
final IOptionHolder delegate = delegates.next();
result = delegate.getLocalOption(key);
if (result != null && key.validForDelegator(result, this))
{
break;
}
}
delegates.release();
depthReference[0] = result != null ? depth : Integer.MAX_VALUE;
return result;
}
}