/*
*
* * Copyright (c) 2016. David Sowerby
* *
* * 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 uk.q3c.krail.core.persist.common.option;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import uk.q3c.krail.core.data.OptionElementConverter;
import uk.q3c.krail.core.data.OptionListConverter;
import uk.q3c.krail.core.option.Option;
import uk.q3c.krail.core.option.OptionException;
import uk.q3c.krail.core.option.OptionKeyException;
import uk.q3c.krail.core.option.OptionList;
import uk.q3c.krail.core.persist.cache.option.DefaultOptionCacheLoader;
import uk.q3c.krail.core.persist.cache.option.OptionCache;
import uk.q3c.krail.core.persist.cache.option.OptionCacheKey;
import uk.q3c.krail.core.persist.inmemory.option.DefaultInMemoryOptionStore;
import uk.q3c.krail.core.persist.inmemory.option.InMemoryOptionStore;
import uk.q3c.krail.core.user.profile.RankOption;
import javax.annotation.Nonnull;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Data Access Object for {@link DefaultInMemoryOptionStore}
* <br>
* <b>NOTE:</b> All values to and from {@link Option} are natively typed. All values to and from {@link OptionCache}, {@link DefaultOptionCacheLoader} and
* {@link OptionDao} are wrapped in Optional.
* <br>
* Created by David Sowerby on 20/02/15.
*/
@SuppressFBWarnings("FCBL_FIELD_COULD_BE_LOCAL") //injected
public class DefaultOptionDao implements OptionDao {
private InMemoryOptionStore optionStore;
private OptionElementConverter optionElementConverter;
private OptionDaoDelegate delegate;
@Inject
public DefaultOptionDao(OptionElementConverter optionElementConverter, OptionSource delegateSource) {
this.optionElementConverter = optionElementConverter;
this.delegate = delegateSource.getActiveDao();
}
/**
* Write the key value pair
*
* @param cacheKey specifies the hierarchy, rank and OptionKey to write to
* @param value the value to write
* @param <V> the value type
*/
@Override
public <V> void write(@Nonnull OptionCacheKey<V> cacheKey, @Nonnull Optional<V> value) {
checkRankOption(cacheKey, RankOption.SPECIFIC_RANK);
checkArgument(value.isPresent(), "Value cannot be empty");
checkNotNull(value);
String stringValue = optionElementConverter.convertValueToString(value.get());
delegate.write(cacheKey, stringValue);
}
@Nonnull
@Override
public <V> Optional<String> deleteValue(@Nonnull OptionCacheKey<V> cacheKey) {
checkRankOption(cacheKey, RankOption.SPECIFIC_RANK);
return delegate.deleteValue(cacheKey);
}
@SuppressWarnings("unchecked")
@Nonnull
@Override
public <V> Optional<V> getValue(@Nonnull OptionCacheKey<V> cacheKey) {
Optional<String> optionalStringValue;
switch (cacheKey.getRankOption()) {
case HIGHEST_RANK:
optionalStringValue = getRankedValue(cacheKey, false);
break;
case LOWEST_RANK:
optionalStringValue = getRankedValue(cacheKey, true);
break;
case SPECIFIC_RANK:
optionalStringValue = getStringValue(cacheKey);
break;
default:
throw new OptionException("Unrecognised rankOption");
}
if (optionalStringValue.isPresent()) {
V defaultValue = cacheKey.getOptionKey()
.getDefaultValue();
if (defaultValue instanceof OptionList) {
OptionList convertedValue = new OptionListConverter(optionElementConverter).convertToModel((OptionList) defaultValue,
optionalStringValue.get());
return Optional.of((V) convertedValue);
} else {
Class<V> elementClass = (Class<V>) defaultValue.getClass();
return Optional.of(optionElementConverter.convertStringToValue(elementClass, optionalStringValue.get()));
}
} else {
return Optional.empty();
}
}
protected Optional<String> getStringValue(@Nonnull OptionCacheKey cacheKey) {
return delegate.getValue(cacheKey);
}
@Nonnull
protected <V> Optional<String> getRankedValue(@Nonnull OptionCacheKey<V> cacheKey, boolean lowest) {
ImmutableList<String> ranks = cacheKey.getHierarchy()
.ranksForCurrentUser();
ImmutableList<String> ranksToUse = (lowest) ? ranks.reverse() : ranks;
for (String rank : ranksToUse) {
OptionCacheKey<V> specificKey = new OptionCacheKey<>(cacheKey, rank, RankOption.SPECIFIC_RANK);
Optional<String> stringValue = getStringValue(specificKey);
if (stringValue.isPresent()) return stringValue;
}
return Optional.empty();
}
@Override
public String connectionUrl() {
return delegate.connectionUrl();
}
/**
* {@inheritDoc}
*/
@Override
public long clear() {
long count = delegate.count();
delegate.clear();
return count;
}
/**
* {@inheritDoc}
*/
@Override
public long count() {
return delegate.count();
}
@Override
public void checkRankOption(OptionCacheKey<?> cacheKey, RankOption expected) {
if (cacheKey.getRankOption() != expected) {
throw new OptionKeyException("OptionCacheKey should have RankOption of: " + expected);
}
}
}