/******************************************************************************* * Copyright 2014 Trevor Robinson * * 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.scurrilous.circe.impl; import java.util.EnumSet; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import com.scurrilous.circe.Hash; import com.scurrilous.circe.HashParameters; import com.scurrilous.circe.HashProvider; import com.scurrilous.circe.HashSupport; import com.scurrilous.circe.IncrementalIntHash; import com.scurrilous.circe.IncrementalLongHash; import com.scurrilous.circe.StatefulHash; import com.scurrilous.circe.StatefulIntHash; import com.scurrilous.circe.StatefulLongHash; import com.scurrilous.circe.StatelessHash; import com.scurrilous.circe.StatelessIntHash; import com.scurrilous.circe.StatelessLongHash; /** * Base implementation for hash function providers. * * @param <P> base supported hash parameters type */ public abstract class AbstractHashProvider<P extends HashParameters> implements HashProvider { private final Class<P> parametersClass; /** * Constructs a new {@link AbstractHashProvider} with the given base * parameters class. * * @param parametersClass the base hash parameters class supported */ protected AbstractHashProvider(Class<P> parametersClass) { this.parametersClass = parametersClass; } @Override public final EnumSet<HashSupport> querySupport(HashParameters params) { if (!parametersClass.isAssignableFrom(params.getClass())) return EnumSet.noneOf(HashSupport.class); return querySupportTyped(parametersClass.cast(params)); } /** * Implemented by subclasses to provide information about the available * implementations corresponding to the given hash algorithm parameters. * Called by {@link #querySupport} if the hash parameters match the base * type supported by this provider. * * @param params the hash algorithm parameters * @return a set of flags indicating the level of support */ protected abstract EnumSet<HashSupport> querySupportTyped(P params); /** * Requests a hash function using the given parameters and support flags. * This method is only responsible for checking support flags returned by * {@link #querySupportTyped}. * <p> * To support caching of stateless hash functions, call * {@link #getCacheable} from this method and implement * {@link #createCacheable}. * * @param params the hash algorithm parameters * @param required the required hash support flags * @return a hash function * @throws UnsupportedOperationException if this provider cannot support the * given parameters */ protected abstract Hash get(P params, EnumSet<HashSupport> required); /** * Called by implementations that support caching of stateless hash * functions when a cached instance is desired. If a cached instance is not * available, this method calls {@link #createCacheable} to create one, * which is then cached (if caching is available). * * @param params the hash algorithm parameters * @param required the required hash support flags * @return a hash function * @throws UnsupportedOperationException if this provider cannot support the * given parameters */ protected final Hash getCacheable(final P params, final EnumSet<HashSupport> required) { if (HashCacheLoader.hasCache()) { final HashCache cache = HashCacheLoader.getCache(); try { return cache.get(params, required, new Callable<Hash>() { @Override public Hash call() throws Exception { return createCacheable(params, required); } }); } catch (ExecutionException e) { final Throwable cause = e.getCause(); if (cause instanceof RuntimeException) throw (RuntimeException) cause; throw new UnsupportedOperationException(e); } } return createCacheable(params, required); } /** * Called by {@link #getCacheable} to create new cacheable stateless hash * functions. The default implementation simply throws * {@link UnsupportedOperationException}. * * @param params the hash algorithm parameters * @param required the required hash support flags * @return a stateless hash function * @throws UnsupportedOperationException if this provider cannot support the * given parameters */ protected StatelessHash createCacheable(P params, EnumSet<HashSupport> required) { throw new UnsupportedOperationException(); } private Hash castAndGet(HashParameters params, EnumSet<HashSupport> required) { if (!parametersClass.isAssignableFrom(params.getClass())) throw new UnsupportedOperationException(); return get(parametersClass.cast(params), required); } @Override public StatefulHash createStateful(HashParameters params) { final Hash hash = castAndGet(params, EnumSet.of(HashSupport.STATEFUL)); if (hash instanceof StatefulHash) return (StatefulHash) hash; if (hash instanceof StatelessHash) return ((StatelessHash) hash).createStateful(); throw new UnsupportedOperationException(); } @Override public StatelessIntHash getStatelessInt(HashParameters params) { final Hash hash = castAndGet(params, EnumSet.of(HashSupport.INT_SIZED)); if (hash instanceof StatelessIntHash) return (StatelessIntHash) hash; if (hash instanceof StatefulIntHash) return ((StatefulIntHash) hash).asStateless(); throw new UnsupportedOperationException(); } @Override public StatelessLongHash getStatelessLong(HashParameters params) { final Hash hash = castAndGet(params, EnumSet.of(HashSupport.LONG_SIZED)); if (hash instanceof StatelessLongHash) return (StatelessLongHash) hash; if (hash instanceof StatefulLongHash) return ((StatefulLongHash) hash).asStateless(); if (hash instanceof StatelessIntHash) return new IntStatelessLongHash((StatelessIntHash) hash); if (hash instanceof StatefulIntHash) return new IntStatelessLongHash(((StatefulIntHash) hash).asStateless()); throw new UnsupportedOperationException(); } @Override public IncrementalIntHash getIncrementalInt(HashParameters params) { final Hash hash = castAndGet(params, EnumSet.of(HashSupport.INT_SIZED, HashSupport.STATELESS_INCREMENTAL)); if (hash instanceof IncrementalIntHash) return (IncrementalIntHash) hash; throw new UnsupportedOperationException(); } @Override public IncrementalLongHash getIncrementalLong(HashParameters params) { final Hash hash = castAndGet(params, EnumSet.of(HashSupport.LONG_SIZED, HashSupport.STATELESS_INCREMENTAL)); if (hash instanceof IncrementalLongHash) return (IncrementalLongHash) hash; throw new UnsupportedOperationException(); } }