001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.message;
018
019import java.util.Arrays;
020import java.util.Objects;
021
022import org.apache.logging.log4j.util.PerformanceSensitive;
023
024/**
025 * Reusable parameterized message. This message is mutable and is not safe to be accessed or modified by multiple
026 * threads concurrently.
027 *
028 * @see ParameterizedMessage
029 * @since 2.6
030 */
031@PerformanceSensitive("allocation")
032public class ReusableParameterizedMessage implements ReusableMessage {
033
034    private static final long serialVersionUID = 7800075879295123856L;
035    private static ThreadLocal<StringBuilder> buffer = new ThreadLocal<>();
036
037    private String messagePattern;
038    private int argCount;
039    private int usedCount;
040    private final int[] indices = new int[256];
041    private transient Object[] varargs;
042    private transient Object[] params = new Object[10];
043    private transient Throwable throwable;
044
045    /**
046     * Creates a reusable message.
047     */
048    public ReusableParameterizedMessage() {
049    }
050
051    private Object[] getTrimmedParams() {
052        return varargs == null ? Arrays.copyOf(params, argCount) : varargs;
053    }
054
055    private Object[] getParams() {
056        return varargs == null ? params : varargs;
057    }
058
059    // see interface javadoc
060    @Override
061    public Object[] swapParameters(final Object[] emptyReplacement) {
062        Object[] result;
063        if (varargs == null) {
064            result = params;
065            params = Objects.requireNonNull(emptyReplacement);
066        } else {
067            result = varargs;
068            varargs = Objects.requireNonNull(emptyReplacement);
069        }
070        return result;
071    }
072
073    // see interface javadoc
074    @Override
075    public short getParameterCount() {
076        return (short) argCount;
077    }
078
079    @Override
080    public Message memento() {
081        return new ParameterizedMessage(messagePattern, getTrimmedParams());
082    }
083
084    private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
085        this.varargs = null;
086        this.messagePattern = messagePattern;
087        this.argCount = argCount;
088        int placeholderCount = count(messagePattern, indices);
089        initThrowable(paramArray, argCount, placeholderCount);
090        this.usedCount = Math.min(placeholderCount, argCount);
091    }
092
093    private static int count(final String messagePattern, final int[] indices) {
094        try {
095            // try the fast path first
096            return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
097        } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
098            return ParameterFormatter.countArgumentPlaceholders(messagePattern);
099        }
100    }
101
102    private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
103        if (usedParams < argCount && this.throwable == null && params[argCount - 1] instanceof Throwable) {
104            this.throwable = (Throwable) params[argCount - 1];
105        }
106    }
107
108    ReusableParameterizedMessage set(String messagePattern, Object... arguments) {
109        init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
110        varargs = arguments;
111        return this;
112    }
113
114    ReusableParameterizedMessage set(String messagePattern, Object p0) {
115        params[0] = p0;
116        init(messagePattern, 1, params);
117        return this;
118    }
119
120    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1) {
121        params[0] = p0;
122        params[1] = p1;
123        init(messagePattern, 2, params);
124        return this;
125    }
126
127    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2) {
128        params[0] = p0;
129        params[1] = p1;
130        params[2] = p2;
131        init(messagePattern, 3, params);
132        return this;
133    }
134
135    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3) {
136        params[0] = p0;
137        params[1] = p1;
138        params[2] = p2;
139        params[3] = p3;
140        init(messagePattern, 4, params);
141        return this;
142    }
143
144    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4) {
145        params[0] = p0;
146        params[1] = p1;
147        params[2] = p2;
148        params[3] = p3;
149        params[4] = p4;
150        init(messagePattern, 5, params);
151        return this;
152    }
153
154    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) {
155        params[0] = p0;
156        params[1] = p1;
157        params[2] = p2;
158        params[3] = p3;
159        params[4] = p4;
160        params[5] = p5;
161        init(messagePattern, 6, params);
162        return this;
163    }
164
165    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
166            Object p6) {
167        params[0] = p0;
168        params[1] = p1;
169        params[2] = p2;
170        params[3] = p3;
171        params[4] = p4;
172        params[5] = p5;
173        params[6] = p6;
174        init(messagePattern, 7, params);
175        return this;
176    }
177
178    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
179            Object p6, Object p7) {
180        params[0] = p0;
181        params[1] = p1;
182        params[2] = p2;
183        params[3] = p3;
184        params[4] = p4;
185        params[5] = p5;
186        params[6] = p6;
187        params[7] = p7;
188        init(messagePattern, 8, params);
189        return this;
190    }
191
192    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
193            Object p6, Object p7, Object p8) {
194        params[0] = p0;
195        params[1] = p1;
196        params[2] = p2;
197        params[3] = p3;
198        params[4] = p4;
199        params[5] = p5;
200        params[6] = p6;
201        params[7] = p7;
202        params[8] = p8;
203        init(messagePattern, 9, params);
204        return this;
205    }
206
207    ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
208            Object p6, Object p7, Object p8, Object p9) {
209        params[0] = p0;
210        params[1] = p1;
211        params[2] = p2;
212        params[3] = p3;
213        params[4] = p4;
214        params[5] = p5;
215        params[6] = p6;
216        params[7] = p7;
217        params[8] = p8;
218        params[9] = p9;
219        init(messagePattern, 10, params);
220        return this;
221    }
222
223    /**
224     * Returns the message pattern.
225     * @return the message pattern.
226     */
227    @Override
228    public String getFormat() {
229        return messagePattern;
230    }
231
232    /**
233     * Returns the message parameters.
234     * @return the message parameters.
235     */
236    @Override
237    public Object[] getParameters() {
238        return getTrimmedParams();
239    }
240
241    /**
242     * Returns the Throwable that was given as the last argument, if any.
243     * It will not survive serialization. The Throwable exists as part of the message
244     * primarily so that it can be extracted from the end of the list of parameters
245     * and then be added to the LogEvent. As such, the Throwable in the event should
246     * not be used once the LogEvent has been constructed.
247     *
248     * @return the Throwable, if any.
249     */
250    @Override
251    public Throwable getThrowable() {
252        return throwable;
253    }
254
255    /**
256     * Returns the formatted message.
257     * @return the formatted message.
258     */
259    @Override
260    public String getFormattedMessage() {
261        final StringBuilder sb = getBuffer();
262        formatTo(sb);
263        return sb.toString();
264    }
265
266    private StringBuilder getBuffer() {
267        StringBuilder result = buffer.get();
268        if (result == null) {
269            final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
270            result = new StringBuilder(Math.min(512, currentPatternLength * 2));
271            buffer.set(result);
272        }
273        result.setLength(0);
274        return result;
275    }
276
277    @Override
278    public void formatTo(final StringBuilder buffer) {
279        if (indices[0] < 0) {
280            ParameterFormatter.formatMessage(buffer, messagePattern, getParams(), argCount);
281        } else {
282            ParameterFormatter.formatMessage2(buffer, messagePattern, getParams(), usedCount, indices);
283        }
284    }
285
286
287    @Override
288    public String toString() {
289        return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
290                Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
291    }
292}