001/*
002 * ============================================================================
003 * Copyright © 2002-2024 by Thomas Thrien.
004 * All Rights Reserved.
005 * ============================================================================
006 *
007 * Licensed to the public under the agreements of the GNU Lesser General Public
008 * License, version 3.0 (the "License"). You may obtain a copy of the License at
009 *
010 *      http://www.gnu.org/licenses/lgpl.html
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
014 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
015 * License for the specific language governing permissions and limitations
016 * under the License.
017 */
018
019package org.tquadrat.foundation.lang;
020
021import static org.apiguardian.api.API.Status.STABLE;
022
023import java.io.Serial;
024import java.util.Optional;
025import java.util.concurrent.locks.Condition;
026import java.util.concurrent.locks.Lock;
027
028import org.apiguardian.api.API;
029import org.tquadrat.foundation.annotation.ClassVersion;
030import org.tquadrat.foundation.lang.internal.AutoLockImpl;
031
032/**
033 *  <p>{@summary A wrapper for locks that supports the
034 *  {@code try-with-resources} feature of Java&nbsp;7.}</p>
035 *  <p>Instead of</p>
036 *  <div class="source-container"><pre>  m_Lock.lock();
037 *  try
038 *  {
039 *      &hellip;
040 *  }
041 *  finally
042 *  {
043 *      m_Lock.unlock();
044 *  }</pre></div>
045 *  <p>you can write now</p>
046 *  <div class="source-container"><pre>  private final AutoLock m_AutoLock = AutoLock.of();
047 *
048 *  &hellip;
049 *
050 *  try( final var ignored = m_AutoLock.lock() )
051 *  {
052 *      &hellip;
053 *  }</pre></div>
054 *  <p>The creation of the local reference to the wrapper object means some
055 *  overhead but in very most scenarios this is negligible.</p>
056 *  <p>{@code AutoLock} will only expose the methods
057 *  {@link #lock()}
058 *  and
059 *  {@link #lockInterruptibly()}
060 *  of the interface
061 *  {@link java.util.concurrent.locks.Lock Lock},
062 *  but with a return value. Exposing other methods is not reasonable.</p>
063 *  <p>Calling
064 *  {@link #close()}
065 *  on the {@code AutoLock} instance or
066 *  {@link Lock#unlock()}
067 *  on the wrapped {@code Lock} object inside the {@code try} block may cause
068 *  unpredictable effects.</p>
069 *
070 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
071 *  @version $Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $
072 *  @since 0.1.0
073 *
074 *  @see java.util.concurrent.locks.Lock
075 *
076 *  @UMLGraph.link
077 */
078@ClassVersion( sourceVersion = "$Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $" )
079@API( status = STABLE, since = "0.1.0" )
080public sealed interface AutoLock extends AutoCloseable
081    permits org.tquadrat.foundation.lang.internal.AutoLockImpl
082{
083        /*---------------*\
084    ====** Inner Classes **====================================================
085        \*---------------*/
086    /**
087     *  <p>{@summary This exception is thrown when an operation fails.} The
088     *
089     *  @version $Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $
090     *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
091     *  @UMLGraph.link
092     *  @since 0.1.0
093     */
094    @SuppressWarnings( "InnerClassOfInterface" )
095    @ClassVersion( sourceVersion = "$Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $" )
096    @API( status = STABLE, since = "0.1.0" )
097    public static final class ExecutionFailedException extends RuntimeException
098    {
099            /*------------------------*\
100        ====** Static Initialisations **=======================================
101            \*------------------------*/
102        /**
103         *  The serial version UID for objects of this class: {@value}.
104         *
105         *  @hidden
106         */
107        @Serial
108        private static final long serialVersionUID = 1L;
109
110            /*--------------*\
111        ====** Constructors **=================================================
112            \*--------------*/
113        /**
114         *  Creates a new instance of {@code ExecutionFailedException} from the
115         *  cause of the failure.
116         *
117         *  @param  cause   The failure cause.
118         */
119        public ExecutionFailedException( final Throwable cause ) { super( cause ); }
120    }   //  class ExecutionFailedException
121
122        /*---------*\
123    ====** Methods **==========================================================
124        \*---------*/
125    /**
126     *  {@inheritDoc}
127     */
128    @Override
129    public void close();
130
131    /**
132     *  Evaluates the given
133     *  {@link Constraint}
134     *  after obtaining the lock, and returns its result.
135     *
136     *  @param  constraint    The constraint
137     *  @return The evaluation result.
138     *  @throws ExecutionFailedException    The evaluation failed for some
139     *      reason.
140     */
141    @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" )
142    public boolean evaluate( final Constraint constraint ) throws ExecutionFailedException;
143
144    /**
145     *  Executes the given action after obtaining the lock.
146     *
147     *  @param  action  The action.
148     *  @throws ExecutionFailedException    The action failed for some reason.
149     *  @deprecated Use
150     *      {@link #perform(Action)}
151     *      instead; basically this method was renamed to avoid the unintended
152     *      use of
153     *      {@link #execute(Operation)}
154     *      when an
155     *      {@link Action}
156     *      should be used instead.
157     */
158    @Deprecated( since = "0.25.3", forRemoval = true )
159    public default void execute( final Action action ) throws ExecutionFailedException
160    {
161        perform( action );
162    }   //  execute()
163
164    /**
165     *  Executes the given operation after obtaining the lock, and returns its
166     *  result.
167     *
168     *  @param  <R> The type of the operation's result.
169     *  @param  operation   The operation.
170     *  @return An instance of
171     *      {@link Optional}
172     *      that holds the result of the operation.
173     *  @throws ExecutionFailedException    The operation failed for some
174     *      reason.
175     */
176    public <R> Optional<R> execute( final Operation<? extends R> operation ) throws ExecutionFailedException;
177
178    /**
179     *  Returns the wrapped lock.
180     *
181     *  @return The wrapped lock.
182     */
183    public Lock getWrappedLockInstance();
184
185    /**
186     *  Calls
187     *  {@link java.util.concurrent.locks.Lock#lock() lock()}
188     *  on the wrapped
189     *  {@link java.util.concurrent.locks.Lock}
190     *  instance.
191     *
192     *  @return The reference to this {@code AutoLock} instance.
193     */
194    public AutoLock lock();
195
196    /**
197     *  Calls
198     *  {@link java.util.concurrent.locks.Lock#lockInterruptibly() lockInterruptibly()}
199     *  on the wrapped
200     *  {@link java.util.concurrent.locks.Lock}
201     *  instance.
202     *
203     *  @return The reference to this {@code AutoLock} instance.
204     *  @throws InterruptedException The current thread was interrupted while
205     *      acquiring the lock (and interruption of lock acquisition is
206     *      supported).
207     */
208    public AutoLock lockInterruptibly() throws InterruptedException;
209
210    /**
211     *  Returns a new
212     *  {@link Condition}
213     *  instance that is bound to the instance of
214     *  {@link java.util.concurrent.locks.Lock}
215     *  that is wrapped by this {@code AutoLock} instance.
216     *
217     *  @return The new condition.
218     *  @throws UnsupportedOperationException   The wrapped lock's
219     *      implementation does not support conditions.
220     *
221     *  @see  java.util.concurrent.locks.Lock#newCondition()
222     */
223    public Condition newCondition();
224
225    /**
226     *  Creates a new {@code AutoLock} instance with an internal lock object.
227     *
228     *  @return The new instance.
229     */
230    @API( status = STABLE, since = "0.0.5" )
231    public static AutoLock of() { return new AutoLockImpl(); }
232
233    /**
234     *  Creates a new {@code AutoLock} instance from the given
235     *  {@link Lock}
236     *  instance.
237     *
238     *  @param  lock    The wrapped lock.
239     *  @return The new instance.
240     */
241    @API( status = STABLE, since = "0.0.5" )
242    public static AutoLock of( final Lock lock ) { return new AutoLockImpl( lock ); }
243
244    /**
245     *  <p>{@summary Performs the given action after obtaining the lock.}</p>
246     *  <p>This differs from
247     *  {@link #execute(Operation)}
248     *  as
249     *  {@link Action}
250     *  does not return something, and a warning regarding &quot;Result of
251     *  assignment expression used&quot; can be avoided in case the
252     *  {@code action} is used to set an attribute.</p>
253     *
254     *  @param  action  The action.
255     *  @throws ExecutionFailedException    The action failed for some reason.
256     */
257    public void perform( final Action action ) throws ExecutionFailedException;
258}
259//  interface AutoLock
260
261/*
262 *  End of File
263 */