All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zepben.util.scoping.Scope Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
/*
 * Copyright 2020 Zeppelin Bend Pty Ltd
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package com.zepben.util.scoping;

import javax.annotation.Nullable;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * Class that can run a block of code within a "scope" of a resource.
 * 

* It basically wraps up the following pattern: *

*
{@code
 * resource.acquire();
 * try {
 *     // do some work while the resource is held
 * } finally {
 *     resource.release();
 * }
 * }
 * 
*
* You might argue that pattern is fine, however the niceness of this class comes from the * {@link Scope#supply(Function)} method. In the above try/finally syntax, it gets messy if you want to calculate a * value while the resource is held, then release the resource to use the value. You either need to release the * resource in the try block (and either double release due to the finally block or track it has already been released) * or declare a variable outside the try block and use it after. *

* Which is nicer? *

* Stopping double release: *

*
{@code
 * bool released = false;
 * resource.acquire();
 * try {
 *     int nextValue = resource.nextValue();
 *     resource.release();
 *     released = true;
 *
 *     // do something with nextValue
 * } finally {
 *     if (!released) {
 *         resource.release();
 *     }
 * }
 * }
 * 
*
* Declaring outside try *
*
{@code
 * int nextValue;
 * resource.acquire();
 * try {
 *     nextValue = resource.nextValue();
 * } finally {
 *     resource.release();
 * }
 *
 * // do something with nextValue
 * }
 * 
*
* or *
*
{@code
 * // As a member of your class:
 * Scope scope = Scope.alwaysAcquires(resource, resource::acquire, resource::release);
 *
 * // In a method acquiring the resource
 * int nextValue = scope.supply(res -> res.nextValue());
 * // do something with nextValue
 * }
 * 
*
* * @param The type of resource held by the scope. */ @SuppressWarnings("WeakerAccess") public class Scope { private final T resource; private final Predicate enter; private final Consumer exit; /** * Create a new Scope instance. *

* The {@code enter} is a {@link Predicate} that takes the resources. If the resource can be acquired, this should * return true, otherwise false. *

* The {@code exit} consumer is used to release your resource after the scope block has been executed. * * @param resource The resource to acquire / release. * @param enter callback to acquire the resource. * @param exit callback to release the resource. */ public Scope(T resource, Predicate enter, Consumer exit) { this.resource = resource; this.enter = enter; this.exit = exit; } /** * Creates a new instance that can always acquire the resource. *

* Because it can always acquire, the {@code enter} is a supplier that is then wrapped in a predicate that always * returns true. *

* This factory method will allow you to instantiate instances in a concise fashion, e.g.: *

*
{@code
     * Scope scope = Scope.alwaysAcquires(resource, resource::acquire, resource::release);
     * }
     * 
*
* * @param resource The resource to acquire / release. * @param enter callback to acquire the resource. * @param exit callback to release the resource. * @param The type of resource held by the scope. * @return A new scope instance where the resource can always be acquired. */ public static Scope alwaysAcquires(T resource, Consumer enter, Consumer exit) { return new Scope<>( resource, r -> { enter.accept(r); return true; }, exit); } /** * Returns the resource contained by the scope. * * @return The resource contained by this scope. */ public T resource() { return resource; } /** * Acquires the resource, executes the provided code, and releases the resource when finished. *

* If the resource can not be acquired, the provided code block will never be executed. * * @param block The code to be executed within the scope. * @return true if the resource was acquired, otherwise false. */ public boolean run(Consumer block) { if (enter.test(resource)) { try { block.accept(resource); return true; } finally { exit.accept(resource); } } return false; } /** * Behaves the same as {@link Scope#run(Consumer)}, except returns a value returned by the provided * code block. *

* If the resource cannot be acquired, the block is never executed and null is returned. * * @param block The code to be executed within the scope, returning a value to be returned by this method. * @param The return type of the code block. * @return The return value of the supplied block if the resource was acquired, otherwise null. */ @Nullable public R supply(Function block) { if (enter.test(resource)) { try { return block.apply(resource); } finally { exit.accept(resource); } } return null; } /** * Behaves exactly the same as {@link Scope#supply(Function)} but the result is wrapped in an {@link Optional}. * * @param block The code to be executed within the scope, returning a value to be returned by this method. * @param The return type of the code block. * @return The return value of the supplied block, wrapped in an Optional. */ public Optional supplyOptionally(Function block) { return Optional.ofNullable(supply(block)); } /** * Allows use of this scope in a try-with-resources block. This may be useful when using lambdas or method * references is undesirable for some reason. *

* The {@link ScopeTwr} returned is an {@link AutoCloseable} that holds the resource of the scope. You can check if * the resource was required by calling {@link ScopeTwr#acquiredResource()} within the TWR block. If the resource * was not acquired, it will not attempt to close it at the end of the TWR block. *

{@code
     * try (ScopeTwr twr = scope.twr()) {
     *     if (twr.acquiredResource()) {
     *         String id = twr.resource().nextId();
     *         ...
     *     }
     * }
     * }
* * @return A {@link ScopeTwr} instance containing the resource of this scope. */ public ScopeTwr twr() { return new ScopeTwr<>(resource, enter.test(resource), exit); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy