/******************************************************************************* * 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.options; import java.io.Serializable; import java.util.AbstractCollection; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.eclipse.jdt.annotation.Nullable; import com.google.common.collect.UnmodifiableIterator; /** * Provides standard implementation of {@link IOptionHolder} methods. * <p> * Extends {@link AbstractOptionHolder} with local options and additional * methods for getting and setting options on this object. * <p> * @since 0.07 * @author Christopher Barber */ public class LocalOptionHolder extends AbstractOptionHolder { /*------- * State */ private volatile @Nullable ConcurrentMap<IOptionKey<?>,Object> _localOptions = null; /*-------------- * Construction */ public LocalOptionHolder() { } /** * Copies options from other option holder * @since 0.08 */ protected LocalOptionHolder(LocalOptionHolder other) { final ConcurrentMap<IOptionKey<?>,Object> otherMap = other._localOptions; if (otherMap != null) { _localOptions = new ConcurrentHashMap<>(otherMap); } } /*--------------- * IOptionHolder */ /** * {@inheritDoc} * <p> * This implementation removes the underlying local option map. */ @Override public void clearLocalOptions() { _localOptions = null; } @Override public Collection<IOption<? extends Serializable>> getLocalOptions() { final ConcurrentMap<IOptionKey<?>,Object> localOptions = _localOptions; if (localOptions == null) { return Collections.emptyList(); } return new AbstractCollection<IOption<? extends Serializable>>() { @Override public Iterator<IOption<? extends Serializable>> iterator() { return new UnmodifiableIterator<IOption<? extends Serializable>>() { final Iterator<Map.Entry<IOptionKey<?>,Object>> _iter = localOptions.entrySet().iterator(); @Override public boolean hasNext() { return _iter.hasNext(); } @Override public IOption<? extends Serializable> next() { final Map.Entry<IOptionKey<?>,Object> entry = _iter.next(); return Option.create(entry.getKey(), entry.getValue()); } }; } @Override public int size() { return localOptions.size(); } }; } @Override @Nullable public <T extends Serializable> T getLocalOption(IOptionKey<T> key) { Map<IOptionKey<?>,Object> options = getLocalOptionMap(false); if (options != null) { final Object value = options.get(key); if (value != null) { return key.type().cast(value); } } return null; } @Override public <T extends Serializable> void setOption(IOptionKey<T> key, T value) { createLocalOptionMap().put(key, key.validate(value, this)); } /** * {@inheritDoc} * <p> * This implementation returns true. */ @Override public boolean supportsLocalOptions() { return true; } @Override public void unsetOption(IOptionKey<?> key) { Map<IOptionKey<?>,Object> map = getLocalOptionMap(false); if (map != null) { map.remove(key); } } /*------------------- * Protected methods */ protected ConcurrentMap<IOptionKey<?>, Object> createLocalOptionMap() { ConcurrentMap<IOptionKey<?>, Object> localOptions = _localOptions; // This uses the double-checked locking idiom. It is only guaranteed to be correct // if the underlying variable is volatile. The second check inside the synchronized // block is necessary because another thread could have created the options and // added an option before this thread gets to hold the lock. if (localOptions == null) { synchronized(this) { localOptions = _localOptions; if (localOptions == null) { _localOptions = localOptions = new ConcurrentHashMap<IOptionKey<?>, Object>(); } } } return localOptions; } protected @Nullable ConcurrentMap<IOptionKey<?>, Object> getLocalOptionMap(boolean create) { return create ? createLocalOptionMap() : _localOptions; } }