001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.log;
018
019import java.util.function.Supplier;
020
021import org.springframework.lang.Nullable;
022import org.springframework.util.Assert;
023
024/**
025 * A simple log message type for use with Commons Logging, allowing
026 * for convenient lazy resolution of a given {@link Supplier} instance
027 * (typically bound to a Java 8 lambda expression) or a printf-style
028 * format string ({@link String#format}) in its {@link #toString()}.
029 *
030 * @author Juergen Hoeller
031 * @since 5.2
032 * @see #of(Supplier)
033 * @see #format(String, Object)
034 * @see #format(String, Object...)
035 * @see org.apache.commons.logging.Log#fatal(Object)
036 * @see org.apache.commons.logging.Log#error(Object)
037 * @see org.apache.commons.logging.Log#warn(Object)
038 * @see org.apache.commons.logging.Log#info(Object)
039 * @see org.apache.commons.logging.Log#debug(Object)
040 * @see org.apache.commons.logging.Log#trace(Object)
041 */
042public abstract class LogMessage implements CharSequence {
043
044        @Nullable
045        private String result;
046
047
048        @Override
049        public int length() {
050                return toString().length();
051        }
052
053        @Override
054        public char charAt(int index) {
055                return toString().charAt(index);
056        }
057
058        @Override
059        public CharSequence subSequence(int start, int end) {
060                return toString().subSequence(start, end);
061        }
062
063        /**
064         * This will be called by the logging provider, potentially once
065         * per log target (therefore locally caching the result here).
066         */
067        @Override
068        public String toString() {
069                if (this.result == null) {
070                        this.result = buildString();
071                }
072                return this.result;
073        }
074
075        abstract String buildString();
076
077
078        /**
079         * Build a lazily resolving message from the given supplier.
080         * @param supplier the supplier (typically bound to a Java 8 lambda expression)
081         * @see #toString()
082         */
083        public static LogMessage of(Supplier<? extends CharSequence> supplier) {
084                return new SupplierMessage(supplier);
085        }
086
087        /**
088         * Build a lazily formatted message from the given format string and argument.
089         * @param format the format string (following {@link String#format} rules)
090         * @param arg1 the argument
091         * @see String#format(String, Object...)
092         */
093        public static LogMessage format(String format, Object arg1) {
094                return new FormatMessage1(format, arg1);
095        }
096
097        /**
098         * Build a lazily formatted message from the given format string and arguments.
099         * @param format the format string (following {@link String#format} rules)
100         * @param arg1 the first argument
101         * @param arg2 the second argument
102         * @see String#format(String, Object...)
103         */
104        public static LogMessage format(String format, Object arg1, Object arg2) {
105                return new FormatMessage2(format, arg1, arg2);
106        }
107
108        /**
109         * Build a lazily formatted message from the given format string and arguments.
110         * @param format the format string (following {@link String#format} rules)
111         * @param arg1 the first argument
112         * @param arg2 the second argument
113         * @param arg3 the third argument
114         * @see String#format(String, Object...)
115         */
116        public static LogMessage format(String format, Object arg1, Object arg2, Object arg3) {
117                return new FormatMessage3(format, arg1, arg2, arg3);
118        }
119
120        /**
121         * Build a lazily formatted message from the given format string and arguments.
122         * @param format the format string (following {@link String#format} rules)
123         * @param arg1 the first argument
124         * @param arg2 the second argument
125         * @param arg3 the third argument
126         * @param arg4 the fourth argument
127         * @see String#format(String, Object...)
128         */
129        public static LogMessage format(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
130                return new FormatMessage4(format, arg1, arg2, arg3, arg4);
131        }
132
133        /**
134         * Build a lazily formatted message from the given format string and varargs.
135         * @param format the format string (following {@link String#format} rules)
136         * @param args the varargs array (costly, prefer individual arguments)
137         * @see String#format(String, Object...)
138         */
139        public static LogMessage format(String format, Object... args) {
140                return new FormatMessageX(format, args);
141        }
142
143
144        private static final class SupplierMessage extends LogMessage {
145
146                private Supplier<? extends CharSequence> supplier;
147
148                SupplierMessage(Supplier<? extends CharSequence> supplier) {
149                        Assert.notNull(supplier, "Supplier must not be null");
150                        this.supplier = supplier;
151                }
152
153                @Override
154                String buildString() {
155                        return this.supplier.get().toString();
156                }
157        }
158
159
160        private static abstract class FormatMessage extends LogMessage {
161
162                protected final String format;
163
164                FormatMessage(String format) {
165                        Assert.notNull(format, "Format must not be null");
166                        this.format = format;
167                }
168        }
169
170
171        private static final class FormatMessage1 extends FormatMessage {
172
173                private final Object arg1;
174
175                FormatMessage1(String format, Object arg1) {
176                        super(format);
177                        this.arg1 = arg1;
178                }
179
180                @Override
181                protected String buildString() {
182                        return String.format(this.format, this.arg1);
183                }
184        }
185
186
187        private static final class FormatMessage2 extends FormatMessage {
188
189                private final Object arg1;
190
191                private final Object arg2;
192
193                FormatMessage2(String format, Object arg1, Object arg2) {
194                        super(format);
195                        this.arg1 = arg1;
196                        this.arg2 = arg2;
197                }
198
199                @Override
200                String buildString() {
201                        return String.format(this.format, this.arg1, this.arg2);
202                }
203        }
204
205
206        private static final class FormatMessage3 extends FormatMessage {
207
208                private final Object arg1;
209
210                private final Object arg2;
211
212                private final Object arg3;
213
214                FormatMessage3(String format, Object arg1, Object arg2, Object arg3) {
215                        super(format);
216                        this.arg1 = arg1;
217                        this.arg2 = arg2;
218                        this.arg3 = arg3;
219                }
220
221                @Override
222                String buildString() {
223                        return String.format(this.format, this.arg1, this.arg2, this.arg3);
224                }
225        }
226
227
228        private static final class FormatMessage4 extends FormatMessage {
229
230                private final Object arg1;
231
232                private final Object arg2;
233
234                private final Object arg3;
235
236                private final Object arg4;
237
238                FormatMessage4(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
239                        super(format);
240                        this.arg1 = arg1;
241                        this.arg2 = arg2;
242                        this.arg3 = arg3;
243                        this.arg4 = arg4;
244                }
245
246                @Override
247                String buildString() {
248                        return String.format(this.format, this.arg1, this.arg2, this.arg3, this.arg4);
249                }
250        }
251
252
253        private static final class FormatMessageX extends FormatMessage {
254
255                private final Object[] args;
256
257                FormatMessageX(String format, Object... args) {
258                        super(format);
259                        this.args = args;
260                }
261
262                @Override
263                String buildString() {
264                        return String.format(this.format, this.args);
265                }
266        }
267
268}