package org.apache.logging.log4j.message;
import java.util.Arrays;
import org.apache.logging.log4j.util.Constants;
import org.apache.logging.log4j.util.PerformanceSensitive;
import org.apache.logging.log4j.util.StringBuilders;
@PerformanceSensitive("allocation")
public class ReusableParameterizedMessage implements ReusableMessage, ParameterVisitable, Clearable {
private static final int MIN_BUILDER_SIZE = 512;
private static final int MAX_PARMS = 10;
private static final long serialVersionUID = 7800075879295123856L;
private transient ThreadLocal<StringBuilder> buffer;
private String messagePattern;
private int argCount;
private int usedCount;
private final int[] indices = new int[256];
private transient Object[] varargs;
private transient Object[] params = new Object[MAX_PARMS];
private transient Throwable throwable;
transient boolean reserved = false;
public ReusableParameterizedMessage() {
}
private Object[] getTrimmedParams() {
return varargs == null ? Arrays.copyOf(params, argCount) : varargs;
}
private Object[] getParams() {
return varargs == null ? params : varargs;
}
@Override
public Object[] swapParameters(final Object[] emptyReplacement) {
Object[] result;
if (varargs == null) {
result = params;
if (emptyReplacement.length >= MAX_PARMS) {
params = emptyReplacement;
} else {
if (argCount <= emptyReplacement.length) {
System.arraycopy(params, 0, emptyReplacement, 0, argCount);
for (int i = 0; i < argCount; i++) {
params[i] = null;
}
result = emptyReplacement;
} else {
params = new Object[MAX_PARMS];
}
}
} else {
if (argCount <= emptyReplacement.length) {
result = emptyReplacement;
} else {
result = new Object[argCount];
}
System.arraycopy(varargs, 0, result, 0, argCount);
}
return result;
}
@Override
public short getParameterCount() {
return (short) argCount;
}
@Override
public <S> void forEachParameter(final ParameterConsumer<S> action, final S state) {
final Object[] parameters = getParams();
for (short i = 0; i < argCount; i++) {
action.accept(parameters[i], i, state);
}
}
@Override
public Message memento() {
return new ParameterizedMessage(messagePattern, getTrimmedParams());
}
private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
this.varargs = null;
this.messagePattern = messagePattern;
this.argCount = argCount;
final int placeholderCount = count(messagePattern, indices);
initThrowable(paramArray, argCount, placeholderCount);
this.usedCount = Math.min(placeholderCount, argCount);
}
private static int count(final String messagePattern, final int[] indices) {
try {
return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
} catch (final Exception ex) {
return ParameterFormatter.countArgumentPlaceholders(messagePattern);
}
}
private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
if (usedParams < argCount && params[argCount - 1] instanceof Throwable) {
this.throwable = (Throwable) params[argCount - 1];
} else {
this.throwable = null;
}
}
ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) {
init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
varargs = arguments;
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0) {
params[0] = p0;
init(messagePattern, 1, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) {
params[0] = p0;
params[1] = p1;
init(messagePattern, 2, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
init(messagePattern, 3, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
params[3] = p3;
init(messagePattern, 4, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
params[3] = p3;
params[4] = p4;
init(messagePattern, 5, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
params[3] = p3;
params[4] = p4;
params[5] = p5;
init(messagePattern, 6, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
final Object p6) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
params[3] = p3;
params[4] = p4;
params[5] = p5;
params[6] = p6;
init(messagePattern, 7, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
final Object p6, final Object p7) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
params[3] = p3;
params[4] = p4;
params[5] = p5;
params[6] = p6;
params[7] = p7;
init(messagePattern, 8, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
final Object p6, final Object p7, final Object p8) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
params[3] = p3;
params[4] = p4;
params[5] = p5;
params[6] = p6;
params[7] = p7;
params[8] = p8;
init(messagePattern, 9, params);
return this;
}
ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
final Object p6, final Object p7, final Object p8, final Object p9) {
params[0] = p0;
params[1] = p1;
params[2] = p2;
params[3] = p3;
params[4] = p4;
params[5] = p5;
params[6] = p6;
params[7] = p7;
params[8] = p8;
params[9] = p9;
init(messagePattern, 10, params);
return this;
}
@Override
public String getFormat() {
return messagePattern;
}
@Override
public Object[] getParameters() {
return getTrimmedParams();
}
@Override
public Throwable getThrowable() {
return throwable;
}
@Override
public String getFormattedMessage() {
final StringBuilder sb = getBuffer();
formatTo(sb);
final String result = sb.toString();
StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
return result;
}
private StringBuilder getBuffer() {
if (buffer == null) {
buffer = new ThreadLocal<>();
}
StringBuilder result = buffer.get();
if (result == null) {
final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
result = new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2));
buffer.set(result);
}
result.setLength(0);
return result;
}
@Override
public void formatTo(final StringBuilder builder) {
if (indices[0] < 0) {
ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount);
} else {
ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices);
}
}
ReusableParameterizedMessage reserve() {
reserved = true;
return this;
}
@Override
public String toString() {
return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
}
@Override
public void clear() {
reserved = false;
varargs = null;
messagePattern = null;
throwable = null;
}
}