extract NotNullizer allowing to hide its own null-object inside

This commit is contained in:
Daniil Ovchinnikov
2019-02-26 16:38:02 +03:00
parent 2798506ec8
commit 14bf8a54fb
3 changed files with 74 additions and 53 deletions
@@ -2,8 +2,6 @@
package com.intellij.util
import com.intellij.openapi.util.RecursionManager
import com.intellij.util.ObjectUtils.notNullize
import com.intellij.util.ObjectUtils.nullize
import java.util.concurrent.atomic.AtomicReference
/**
@@ -12,7 +10,7 @@ import java.util.concurrent.atomic.AtomicReference
class RecursionPreventingSafePublicationLazy<T>(recursionKey: Any?, initializer: () -> T) : Lazy<T?> {
@Volatile
private var initializer: (() -> T)? = { notNullize(initializer()) }
private var initializer: (() -> T)? = { ourNotNullizer.notNullize(initializer()) }
private val valueRef: AtomicReference<T> = AtomicReference()
private val recursionKey: Any = recursionKey ?: this
@@ -20,13 +18,13 @@ class RecursionPreventingSafePublicationLazy<T>(recursionKey: Any?, initializer:
get() {
val computedValue = valueRef.get()
if (computedValue !== null) {
return nullize(computedValue)
return ourNotNullizer.nullize(computedValue)
}
val initializerValue = initializer
if (initializerValue === null) {
// Some thread managed to clear the initializer => it managed to set the value.
return nullize(valueRef.get())
return ourNotNullizer.nullize(valueRef.get())
}
val stamp = ourRecursionGuard.markStack()
@@ -38,16 +36,16 @@ class RecursionPreventingSafePublicationLazy<T>(recursionKey: Any?, initializer:
}
if (!stamp.mayCacheNow()) {
// Recursion occurred somewhere deep.
return nullize(newValue)
return ourNotNullizer.nullize(newValue)
}
if (!valueRef.compareAndSet(null, newValue)) {
// Some thread managed to set the value.
return nullize(valueRef.get())
return ourNotNullizer.nullize(valueRef.get())
}
initializer = null
return nullize(newValue)
return ourNotNullizer.nullize(newValue)
}
override fun isInitialized(): Boolean = valueRef.get() !== null
@@ -56,5 +54,6 @@ class RecursionPreventingSafePublicationLazy<T>(recursionKey: Any?, initializer:
companion object {
private val ourRecursionGuard = RecursionManager.createGuard("RecursionPreventingSafePublicationLazy")
private val ourNotNullizer = NotNullizer("RecursionPreventingSafePublicationLazy")
}
}
@@ -0,0 +1,64 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Has its own {@code null}-object instance, which is hidden from outside world, preventing accidental &quot;unwrapping&quot;.
*/
public final class NotNullizer {
private final Object myNull;
@Contract(pure = true)
public NotNullizer(@NotNull String name) {
myNull = ObjectUtils.sentinel(name);
}
@Contract(pure = true)
@NotNull
private <T> T fakeNull() {
//noinspection unchecked
return (T)myNull;
}
/**
* &quot;Wraps&quot; {@code null} with {@code null}-object.
* <p/>
* Useful when some generic data structure A does not allow {@code null}s,
* but there is a need to implement another structure B on top of A which should support {@code null}s.
* <br/>
* Returned value should never be presented to clients of structure B,
* and it must be &quot;unwrapped&quot; back with {@link #nullize(Object)}.
* <br/>
* Casting the value to anything but {@link Object} will result in {@link ClassCastException},
* this means the value cannot be used anywhere where {@link T} is a specific type different from {@link Object}.
*
* @return {@code null} wrapper if value is {@code null}, otherwise original value
* @see #nullize(Object)
*/
@Contract(value = "!null -> param1", pure = true)
@NotNull
public <T> T notNullize(@Nullable T value) {
if (value == null) {
return fakeNull();
}
else {
return value;
}
}
/**
* &quot;Unwraps&quot; {@code null} from the value returned by {@link #notNullize(Object)}.
*
* @return {@code null} if value is the {@code null} wrapper, otherwise original value
* @see #notNullize(Object)
*/
@Contract(pure = true)
@Nullable
public <T> T nullize(@NotNull T value) {
return value == myNull ? null : value;
}
}
@@ -19,53 +19,11 @@ public class ObjectUtils {
private ObjectUtils() {
}
/**
* @see NotNullizer
*/
public static final Object NULL = sentinel("ObjectUtils.NULL");
@Contract(pure = true)
@NotNull
private static <T> T fakeNull() {
//noinspection unchecked
return (T)NULL;
}
/**
* &quot;Wraps&quot; {@code null} with {@code null}-object.
* <p/>
* Useful when some generic data structure A does not allow {@code null}s,
* but there is a need to implement another structure B on top of A which should support {@code null}s.
* <br/>
* Returned value should never be presented to clients of structure B,
* and it must be &quot;unwrapped&quot; back with {@link #nullize(Object)}.
* <br/>
* Casting the value to anything but {@link Object} will result in {@link ClassCastException},
* this means the value cannot be used anywhere where {@link T} is a specific type different from {@link Object}.
*
* @return {@code null} wrapper if value is {@code null}, otherwise original value
* @see #nullize(Object)
*/
@Contract(value = "!null -> param1", pure = true)
@NotNull
public static <T> T notNullize(@Nullable T value) {
if (value == null) {
return fakeNull();
}
else {
return value;
}
}
/**
* &quot;Unwraps&quot; {@code null} from the value returned by {@link #notNullize(Object)}.
*
* @return {@code null} if value is the {@code null} wrapper, otherwise original value
* @see #notNullize(Object)
*/
@Contract(pure = true)
@Nullable
public static <T> T nullize(@NotNull T value) {
return value == NULL ? null : value;
}
/**
* Creates a new object which could be used as sentinel value (special value to distinguish from any other object). It does not equal
* to any other object. Usually should be assigned to the static final field.