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

lombok.core.handlers.ConditionAndLockHandler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2011-2012 Philipp Eichhorn
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package lombok.core.handlers;

import static lombok.ast.AST.*;
import static lombok.core.util.ErrorMessages.*;
import static lombok.core.util.Names.*;

import java.util.*;
import java.util.concurrent.locks.*;

import lombok.*;
import lombok.ast.*;
import lombok.core.DiagnosticsReceiver;

@RequiredArgsConstructor
public final class ConditionAndLockHandler, METHOD_TYPE extends IMethod> {
	private final TYPE_TYPE type;
	private final METHOD_TYPE method;
	private final DiagnosticsReceiver diagnosticsReceiver;
	private AwaitData await;
	private SignalData signal;
	private String lockMethod;

	public ConditionAndLockHandler withAwait(final AwaitData await) {
		this.await = await;
		return this;
	}

	public ConditionAndLockHandler withSignal(final SignalData signal) {
		this.signal = signal;
		return this;
	}

	public ConditionAndLockHandler withLockMethod(final String lockMethod) {
		this.lockMethod = lockMethod;
		return this;
	}

	public boolean preHandle(final String lockName, final Class annotationType) {
		if (method == null) {
			diagnosticsReceiver.addError(canBeUsedOnMethodOnly(annotationType));
			return false;
		}
		if (method.isAbstract()) {
			diagnosticsReceiver.addError(canBeUsedOnConcreteMethodOnly(annotationType));
			return false;
		}

		boolean isReadWriteLock = lockMethod != null;

		if (!isReadWriteLock && (await == null) && (signal == null)) {
			return false; // wrong configured handler, so better stop here
		}

		String annotationTypeName = annotationType.getSimpleName();
		String completeLockName = createCompleteLockName(lockName, isReadWriteLock);

		if (!tryToAddLockField(completeLockName, isReadWriteLock, annotationTypeName)) return false;

		if (!isReadWriteLock) {
			if (!tryToAddConditionField(await, completeLockName, annotationTypeName)) return false;
			if (!tryToAddConditionField(signal, completeLockName, annotationTypeName)) return false;
		}
		return true;
	}

	public void handle(final String lockName, final Class annotationType, final IParameterValidator validation,
			final IParameterSanitizer sanitizer) {
		if (!preHandle(lockName, annotationType)) return;

		boolean isReadWriteLock = lockMethod != null;

		String annotationTypeName = annotationType.getSimpleName();
		String completeLockName = createCompleteLockName(lockName, isReadWriteLock);

		List> beforeMethodBlock = new ArrayList>();
		List> afterMethodBlock = new ArrayList>();

		if (!isReadWriteLock) {
			if (!getConditionStatements(await, completeLockName, annotationTypeName, beforeMethodBlock, afterMethodBlock)) return;
			if (!getConditionStatements(signal, completeLockName, annotationTypeName, beforeMethodBlock, afterMethodBlock)) return;
		}

		final Call lockCall;
		final Call unLockCall;
		if (isReadWriteLock) {
			lockCall = Call(Call(Field(completeLockName), lockMethod), "lock");
			unLockCall = Call(Call(Field(completeLockName), lockMethod), "unlock");
		} else {
			lockCall = Call(Field(completeLockName), "lock");
			unLockCall = Call(Field(completeLockName), "unlock");
		}

		method.editor().replaceBody(Block().posHint(method.get()) //
				.withStatements(validation.validateParameterOf(method)) //
				.withStatements(sanitizer.sanitizeParameterOf(method)) //
				.withStatement(lockCall) //
				.withStatement(Try(Block() //
						.withStatements(beforeMethodBlock) //
						.withStatements(method.statements()) //
						.withStatements(afterMethodBlock)//
						).Finally(Block() //
								.withStatement(unLockCall) //
						) //
				));

		method.editor().rebuild();
	}

	private String createCompleteLockName(final String lockName, final boolean isReadWriteLock) {
		String completeLockName = lockName;
		if ((!isReadWriteLock) && trim(lockName).isEmpty()) {
			String awaitCondition = trim(await == null ? "" : await.condition);
			String signalCondition = trim(signal == null ? "" : signal.condition);
			completeLockName = "$" + camelCase(awaitCondition, signalCondition, "lock");
		}
		return completeLockName;
	}

	private boolean getConditionStatements(final ConditionData condition, final String lockName, final String annotationTypeName, final List> before,
			final List> after) {
		if (condition == null) {
			return true;
		}
		if (tryToAddConditionField(condition, lockName, annotationTypeName)) {
			switch (condition.pos) {
			case BEFORE:
				before.add(condition.toStatement());
				break;
			default:
			case AFTER:
				after.add(condition.toStatement());
				break;
			}
			return true;
		}
		return false;
	}

	private boolean tryToAddLockField(final String lockName, final boolean isReadWriteLock, final String annotationTypeName) {
		String trimmedLockName = trim(lockName);
		if (trimmedLockName.isEmpty()) {
			diagnosticsReceiver.addError(String.format("@%s 'lockName' may not be empty or null.", annotationTypeName));
			return false;
		}
		if (!type.hasField(trimmedLockName)) {
			if (isReadWriteLock) {
				type.editor().injectField(FieldDecl(Type(ReadWriteLock.class), trimmedLockName).makePrivate().makeFinal() //
						.withInitialization(New(Type(ReentrantReadWriteLock.class))));
			} else {
				type.editor().injectField(FieldDecl(Type(Lock.class), trimmedLockName).makePrivate().makeFinal() //
						.withInitialization(New(Type(ReentrantLock.class))));
			}
		}
		return true;
	}

	private boolean tryToAddConditionField(final ConditionData condition, final String lockName, final String annotationTypeName) {
		if (condition == null) {
			return true;
		}
		String conditionName = trim(condition.condition);
		if (conditionName.isEmpty()) {
			diagnosticsReceiver.addError(String.format("@%s 'conditionName' may not be empty or null.", annotationTypeName));
			return false;
		}
		if (!type.hasField(conditionName)) {
			type.editor().injectField(FieldDecl(Type(Condition.class), conditionName).makePrivate().makeFinal() //
					.withInitialization(Call(Name(lockName), "newCondition")));
		}
		return true;
	}

	public static class AwaitData extends ConditionData {
		protected final String conditionMethod;

		public AwaitData(final String condition, final String conditionMethod, final Position pos) {
			super(condition, pos);
			this.conditionMethod = conditionMethod;
		}

		@Override
		public Statement toStatement() {
			return Try(Block().withStatement(While(Call(This(), conditionMethod)).Do(Call(Field(condition), "await")))) //
					.Catch(Arg(Type(InterruptedException.class), "e"), Block().withStatement(Throw(New(Type(RuntimeException.class)).withArgument(Name("e")))));
		}
	}

	public static class SignalData extends ConditionData {
		public SignalData(final String condition, final Position pos) {
			super(condition, pos);
		}

		@Override
		public Statement toStatement() {
			return Call(Field(condition), "signal");
		}
	}

	@RequiredArgsConstructor
	public abstract static class ConditionData {
		protected final String condition;
		protected final Position pos;

		public abstract Statement toStatement();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy