package org.fluentlenium.core.conditions.message; import org.fluentlenium.core.conditions.Negation; import org.fluentlenium.utils.ReflectionUtils; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Invocation handler that builds message based on called performed. */ public class MessageBuilderInvocationHandler implements InvocationHandler { private Object instance; private final List<MessageBuilderCall> calls; /** * Creates a new message builder invocation handler. * * @param context base context of the generated message */ public MessageBuilderInvocationHandler(String context) { this(new ArrayList<>()); MessageBuilderCall messageBuilderCall = new MessageBuilderCall(); messageBuilderCall.setContext(context); calls.add(messageBuilderCall); } /** * Creates a new message builder invocation handler. * * @param context base context of the generated message * @param instance underlying wrapped instance. If not null, calls will also be performed on this instance. */ public MessageBuilderInvocationHandler(String context, Object instance) { this(context); this.instance = instance; } /** * Creates a new message builder invocation handler, with initial calls. * * @param calls initial calls. */ public MessageBuilderInvocationHandler(List<MessageBuilderCall> calls) { this.calls = calls; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object instanceReturn = null; if (instance != null) { instanceReturn = method.invoke(instance, args); } MessageBuilderCall callItem = new MessageBuilderCall(); if (method.isAnnotationPresent(Message.class)) { callItem.setMessage(method.getAnnotation(Message.class).value()); } if (method.isAnnotationPresent(NotMessage.class)) { callItem.setNotMessage(method.getAnnotation(NotMessage.class).value()); } if (method.isAnnotationPresent(MessageContext.class)) { callItem.setContext(method.getAnnotation(MessageContext.class).value()); } callItem.setArgs(args); if (method.isAnnotationPresent(Negation.class)) { callItem.setNegation(true); } calls.add(callItem); if (!method.getReturnType().isPrimitive()) { return MessageProxy.wrap(method.getReturnType(), instanceReturn, calls); } if (instance == null) { return ReflectionUtils.getDefault(method.getReturnType()); } return instanceReturn; } /** * Build the message based on annotations from methods called previously. * * @return built message * @throws IllegalStateException if one of the recorded call has no @Message/@NotMessage annotation. */ public String buildMessage() { StringBuilder messageBuilder = new StringBuilder(); for (MessageBuilderCall call : calls) { if (call.getContext() != null) { if (messageBuilder.length() > 0) { messageBuilder.append(' '); } messageBuilder.append(call.getContext()); } } boolean negation = false; for (MessageBuilderCall call : calls) { if (call.isNegation()) { negation = !negation; } } List<MessageBuilderCall> reversedCalls = new ArrayList<>(calls); Collections.reverse(reversedCalls); for (MessageBuilderCall call : reversedCalls) { String validationMessage = negation ? call.getMessage() : call.getNotMessage(); if (validationMessage == null) { continue; } validationMessage = MessageFormat.format(validationMessage, call.getArgs()); messageBuilder.append(' '); messageBuilder.append(validationMessage); return messageBuilder.toString(); // NOPMD AvoidBranchingStatementAsLastInLoop } throw new IllegalStateException("No @Message/@NotMessage annotation found in the calls."); } }