From 14bf8a54fba7700a5a9ac20fbdbc0e615d8d8afe Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 26 Feb 2019 16:22:40 +0300 Subject: [PATCH] extract NotNullizer allowing to hide its own null-object inside --- .../RecursionPreventingSafePublicationLazy.kt | 15 ++--- .../src/com/intellij/util/NotNullizer.java | 64 +++++++++++++++++++ .../src/com/intellij/util/ObjectUtils.java | 48 +------------- 3 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 platform/util/src/com/intellij/util/NotNullizer.java diff --git a/platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt b/platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt index 55c3d887f6d3..6528b8e43068 100644 --- a/platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt +++ b/platform/lang-impl/src/com/intellij/util/RecursionPreventingSafePublicationLazy.kt @@ -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(recursionKey: Any?, initializer: () -> T) : Lazy { @Volatile - private var initializer: (() -> T)? = { notNullize(initializer()) } + private var initializer: (() -> T)? = { ourNotNullizer.notNullize(initializer()) } private val valueRef: AtomicReference = AtomicReference() private val recursionKey: Any = recursionKey ?: this @@ -20,13 +18,13 @@ class RecursionPreventingSafePublicationLazy(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(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(recursionKey: Any?, initializer: companion object { private val ourRecursionGuard = RecursionManager.createGuard("RecursionPreventingSafePublicationLazy") + private val ourNotNullizer = NotNullizer("RecursionPreventingSafePublicationLazy") } } diff --git a/platform/util/src/com/intellij/util/NotNullizer.java b/platform/util/src/com/intellij/util/NotNullizer.java new file mode 100644 index 000000000000..582eb768c0ed --- /dev/null +++ b/platform/util/src/com/intellij/util/NotNullizer.java @@ -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 "unwrapping". + */ +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 fakeNull() { + //noinspection unchecked + return (T)myNull; + } + + /** + * "Wraps" {@code null} with {@code null}-object. + *

+ * 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. + *
+ * Returned value should never be presented to clients of structure B, + * and it must be "unwrapped" back with {@link #nullize(Object)}. + *
+ * 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 notNullize(@Nullable T value) { + if (value == null) { + return fakeNull(); + } + else { + return value; + } + } + + /** + * "Unwraps" {@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 nullize(@NotNull T value) { + return value == myNull ? null : value; + } +} diff --git a/platform/util/src/com/intellij/util/ObjectUtils.java b/platform/util/src/com/intellij/util/ObjectUtils.java index b8772bedfb74..f4f0a1f8a7d4 100644 --- a/platform/util/src/com/intellij/util/ObjectUtils.java +++ b/platform/util/src/com/intellij/util/ObjectUtils.java @@ -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 fakeNull() { - //noinspection unchecked - return (T)NULL; - } - - /** - * "Wraps" {@code null} with {@code null}-object. - *

- * 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. - *
- * Returned value should never be presented to clients of structure B, - * and it must be "unwrapped" back with {@link #nullize(Object)}. - *
- * 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 notNullize(@Nullable T value) { - if (value == null) { - return fakeNull(); - } - else { - return value; - } - } - - /** - * "Unwraps" {@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 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.