Skip to content

Commit

Permalink
Merge branch 'master' into functional-shorthands
Browse files Browse the repository at this point in the history
  • Loading branch information
JarvisCraft authored Aug 31, 2021
2 parents 2106953 + 41e5685 commit 6b65fc8
Show file tree
Hide file tree
Showing 28 changed files with 1,323 additions and 64 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ As its dependencies PADLA uses:
- Compiletime:
- [Lombok](https://github.com/rzwitserloot/lombok) for generating boilerplate stuff
- [Jetbrains Annotations](https://github.com/JetBrains/java-annotations) for documenting code logic
- Runtime:
- [Caffeine](https://github.com/ben-manes/caffeine) for caching
- Testing:
- [Junit5](https://github.com/junit-team/junit5/) with related sub-tools for testing
- [Hamcrest](https://github.com/hamcrest/JavaHamcrest) for more creating more readable tests
- [Mockito](https://github.com/mockito/mockito) for mocking in tests
- Additional (these are not inherited by default and are required only if using specific classes):
- [ASM](https://gitlab.ow2.org/asm/asm) for runtime class generation (if using classes annotated with `@UsesBytecodeModification(CommonBytecodeLibrary.ASM)`)
- [Javassist](https://github.com/jboss-javassist/javassist) for runtime class generation (if using classes annotated with `@UsesBytecodeModification(CommonBytecodeLibrary.JAVASSIST)`)
- Optional (these are not required at all but allow some extra integrations and optimizations):
- [Caffeine](https://github.com/ben-manes/caffeine) for caching of internal components
2 changes: 1 addition & 1 deletion java-commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<artifactId>padla</artifactId>
<groupId>ru.progrm-jarvis</groupId>
<version>1.0.0-SNAPSHOT</version>
<version>1.0.0-rc.5</version>
</parent>
<artifactId>java-commons</artifactId>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ru.progrm_jarvis.javacommons.cache;

import lombok.NonNull;
import org.jetbrains.annotations.NotNull;

import java.util.function.Function;

/**
* Simple cache interface.
* {@link #never() No-op implementation} (the one which never performs any caching) is considered correct.
*
* @param <K> type of cache keys
* @param <V> type of cached values
*
* @apiNote currently <b>java-commons</b> does not implement its own functional cache,
* it simply provides a universal interface and wrappers for known implementations
*/
@FunctionalInterface
public interface Cache<K, V> {

/**
* Gets the value from the cache computing it on demand using the provided function.
*
* @param key key by which ti get the value from cache
* @param mappingFunction function used to create the value if there is no cached one
* @return the current value associated with the specified key
*
* @throws NullPointerException if {@code key} is {@code null}
* @throws NullPointerException if {@code mappingFunction} is {@code null}
*/
V get(@NonNull K key, @NonNull Function<? super K, ? extends V> mappingFunction);

/**
* Creates a cache which actually never performs caching.
*
* @param <K> type of cache keys
* @param <V> type of cached values
* @return cache which actually never performs caching
*/
static @NotNull <K, V> Cache<K, V> never() {
return (key, mappingFunction) -> mappingFunction.apply(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package ru.progrm_jarvis.javacommons.cache;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;

/**
* Factory used for creation of generic {@link Cache caches}.
*/
public interface CacheFactory {

/**
* Creates a new cache whose keys will be stored {@link java.lang.ref.WeakReference weakly}.
*
* @param <K> type of cache keys
* @param <V> type of cached values
* @return weak keys cache
*/
<K, V> @NotNull Cache<K, V> weakKeysCache();

/**
* Creates a new cache whose values will be stored {@link java.lang.ref.WeakReference weakly}.
*
* @param <K> type of cache keys
* @param <V> type of cached values
* @return weak values cache
*/
<K, V> @NotNull Cache<K, V> weakValuesCache();

/**
* Creates a new cache whose values will be stored {@link java.lang.ref.SoftReference softly}.
*
* @param <K> type of cache keys
* @param <V> type of cached values
* @return soft values cache
*/
<K, V> @NotNull Cache<K, V> softValuesCache();

/**
* Creates a cache factory which always creates {@link Cache#never() no-op caches}.
*
* @return cache factory which always supplies {@link Cache#never() no-op caches}
*/
static @NotNull CacheFactory never() {
return NeverCacheFactory.INSTANCE;
}

/**
* {@link CacheFactory} which always creates {@link Cache#never() no-op caches}.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
final class NeverCacheFactory implements CacheFactory {

/**
* Singleton instance of this cache factory.
*/
private static final @NotNull CacheFactory INSTANCE = new NeverCacheFactory();

@Override
public <K, V> @NotNull Cache<K, V> weakKeysCache() {
return Cache.never();
}

@Override
public <K, V> @NotNull Cache<K, V> weakValuesCache() {
return Cache.never();
}

@Override
public <K, V> @NotNull Cache<K, V> softValuesCache() {
return Cache.never();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package ru.progrm_jarvis.javacommons.cache;

import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;

/**
* Utility for creating commonly used caches depending on runtime capabilities.
* If no cache is available then {@link Cache#never() no-op cache} is used.
*/
@UtilityClass
public class Caches {

/**
* Factory used for creation of {@link Cache caches}.
* This will try to be the best implementation available
* but will fallback to {@link CacheFactory#never() no-op} if none is available.
*/
private final @NotNull CacheFactory DEFAULT_FACTORY;

static {
final CacheFactory factory;
DEFAULT_FACTORY = (factory = CaffeineCache.tryCreateFactory()) == null ? CacheFactory.never() : factory;
}

/**
* Gets the default cache factory.
*
* @return default cache factory
*/
public @NotNull CacheFactory defaultFactory() {
return DEFAULT_FACTORY;
}

/**
* Creates a new cache whose keys will be stored {@link java.lang.ref.WeakReference weakly}.
*
* @param <K> type of cache keys
* @param <V> type of cached values
* @return weak keys cache
*/
public <K, V> @NotNull Cache<K, V> weakKeysCache() {
return DEFAULT_FACTORY.weakKeysCache();
}

/**
* Creates a new cache whose values will be stored {@link java.lang.ref.WeakReference weakly}.
*
* @param <K> type of cache keys
* @param <V> type of cached values
* @return weak values cache
*/
public <K, V> @NotNull Cache<K, V> weakValuesCache() {
return DEFAULT_FACTORY.weakValuesCache();
}

/**
* Creates a new cache whose values will be stored {@link java.lang.ref.WeakReference softly}.
*
* @param <K> type of cache keys
* @param <V> type of cached values
* @return soft values cache
*/
public <K, V> @NotNull Cache<K, V> softValuesCache() {
return DEFAULT_FACTORY.softValuesCache();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package ru.progrm_jarvis.javacommons.cache;

import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.progrm_jarvis.javacommons.util.BlackHole;

/**
* Utilities for creation of <a href="https://github.com/ben-manes/caffeine">Caffeine</a>-based
* {@link CacheFactory cache factories}.
*/
@UtilityClass
public class CaffeineCache {

/**
* Flag indicating whether {@link Caffeine} is available
*/
private static final boolean AVAILABLE;

static {
boolean available = true;
try { // check if Caffeine class is available
BlackHole.consume(Caffeine.class);
} catch (final Throwable ignored) {
available = false;
}
AVAILABLE = available;
}

/**
* Creates a Caffeine Cache factory.
*
* @return created Caffeine Cache factory
*
* @throws IllegalStateException if Caffeine is not available
*/
public static @NotNull CacheFactory createFactory() {
if (AVAILABLE) return new CaffeineCacheFactory();

throw new IllegalStateException("Caffeine Cache is not available");
}

/**
* Attempts to create a Caffeine Cache factory.
*
* @return created Caffeine Cache factory or {@code null} if it is unavailable
*/
public static @Nullable CacheFactory tryCreateFactory() {
return AVAILABLE ? CaffeineCacheFactory.INSTANCE : null;
}

/**
* {@link CacheFactory} backed by Caffeine Cache.
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
private static final class CaffeineCacheFactory implements CacheFactory {

/**
* Singleton instance of this {@link CacheFactory} implementation
*/
private static final @NotNull CacheFactory INSTANCE = new CaffeineCacheFactory();

/**
* Wraps the provided {@link com.github.benmanes.caffeine.cache.Cache Caffeine Cache} into {@link Cache}.
*
* @param caffeineCache Caffeine Cache to be wrapped
* @param <K> type of cache keys
* @param <V> type of cached values
* @return wrapped Caffeine Cache
*/
private static <K, V> @NotNull Cache<K, V> wrap(
final @NotNull com.github.benmanes.caffeine.cache.Cache<K, V> caffeineCache
) {
return caffeineCache::get;
}

@Override
public <K, V> @NotNull Cache<K, V> weakKeysCache() {
return wrap(Caffeine.newBuilder().weakKeys().build());
}

@Override
public <K, V> @NotNull Cache<K, V> weakValuesCache() {
return wrap(Caffeine.newBuilder().weakValues().build());
}

@Override
public <K, V> @NotNull Cache<K, V> softValuesCache() {
return wrap(Caffeine.newBuilder().softValues().build());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package ru.progrm_jarvis.javacommons.collection;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import javassist.*;
import lombok.NonNull;
import lombok.SneakyThrows;
Expand All @@ -13,6 +11,8 @@
import org.jetbrains.annotations.Unmodifiable;
import ru.progrm_jarvis.javacommons.bytecode.CommonBytecodeLibrary;
import ru.progrm_jarvis.javacommons.bytecode.annotation.UsesBytecodeModification;
import ru.progrm_jarvis.javacommons.cache.Cache;
import ru.progrm_jarvis.javacommons.cache.Caches;
import ru.progrm_jarvis.javacommons.classloading.ClassNamingStrategy;
import ru.progrm_jarvis.javacommons.classloading.GcClassDefiners;
import ru.progrm_jarvis.javacommons.lazy.Lazy;
Expand Down Expand Up @@ -52,8 +52,7 @@ public class CollectionFactory {
/**
* Cache of instances of generated enum sets using naturally sorted array of its elements as the key
*/
private final @NonNull Cache<Enum<?>[], Set<Enum<?>>> IMMUTABLE_ENUM_SETS
= Caffeine.newBuilder().weakValues().build();
private final @NonNull Cache<Enum<?>[], Set<Enum<?>>> IMMUTABLE_ENUM_SETS = Caches.weakValuesCache();

/**
* {@link CtClass} representation of {@link AbstractImmutableSet} wrapped in {@link Lazy}
Expand Down
Loading

0 comments on commit 6b65fc8

Please sign in to comment.