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 */
017 package org.apache.logging.log4j.message;
018
019 import org.apache.logging.log4j.Logger;
020 import org.apache.logging.log4j.status.StatusLogger;
021
022 import java.io.IOException;
023 import java.io.ObjectInputStream;
024 import java.io.ObjectOutputStream;
025 import java.text.Format;
026 import java.text.MessageFormat;
027 import java.util.Arrays;
028 import java.util.regex.Pattern;
029
030 /**
031 * Handles messages that contain a format String. Dynamically determines if the format conforms to
032 * MessageFormat or String.format and if not then uses ParameterizedMessage to format.
033 */
034 public class FormattedMessage implements Message {
035
036 private static final Logger LOGGER = StatusLogger.getLogger();
037
038 private static final long serialVersionUID = -665975803997290697L;
039
040 private static final int HASHVAL = 31;
041
042 private static final String FORMAT_SPECIFIER = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
043
044 private static final Pattern MSG_PATTERN = Pattern.compile(FORMAT_SPECIFIER);
045
046 private String messagePattern;
047 private transient Object[] argArray;
048 private String[] stringArgs;
049 private transient String formattedMessage;
050 private final Throwable throwable;
051
052 private Message message;
053
054 public FormattedMessage(final String messagePattern, final Object[] arguments, final Throwable throwable) {
055 this.messagePattern = messagePattern;
056 this.argArray = arguments;
057 this.throwable = throwable;
058 }
059
060 public FormattedMessage(final String messagePattern, final Object[] arguments) {
061 this.messagePattern = messagePattern;
062 this.argArray = arguments;
063 this.throwable = null;
064 }
065
066 /**
067 * Constructor with a pattern and a single parameter.
068 * @param messagePattern The message pattern.
069 * @param arg The parameter.
070 */
071 public FormattedMessage(final String messagePattern, final Object arg) {
072 this.messagePattern = messagePattern;
073 this.argArray = new Object[] {arg};
074 this.throwable = null;
075 }
076
077 /**
078 * Constructor with a pattern and two parameters.
079 * @param messagePattern The message pattern.
080 * @param arg1 The first parameter.
081 * @param arg2 The second parameter.
082 */
083 public FormattedMessage(final String messagePattern, final Object arg1, final Object arg2) {
084 this(messagePattern, new Object[]{arg1, arg2});
085 }
086
087
088 /**
089 * Returns the formatted message.
090 * @return the formatted message.
091 */
092 public String getFormattedMessage() {
093 if (formattedMessage == null) {
094 if (message == null) {
095 message = getMessage(messagePattern, argArray, throwable);
096 }
097 formattedMessage = message.getFormattedMessage();
098 }
099 return formattedMessage;
100 }
101
102 /**
103 * Returns the message pattern.
104 * @return the message pattern.
105 */
106 public String getFormat() {
107 return messagePattern;
108 }
109
110 /**
111 * Returns the message parameters.
112 * @return the message parameters.
113 */
114 public Object[] getParameters() {
115 if (argArray != null) {
116 return argArray;
117 }
118 return stringArgs;
119 }
120
121 protected Message getMessage(final String msgPattern, final Object[] args, final Throwable throwable) {
122 try {
123 final MessageFormat format = new MessageFormat(msgPattern);
124 final Format[] formats = format.getFormats();
125 if (formats != null && formats.length > 0) {
126 return new MessageFormatMessage(msgPattern, args);
127 }
128 } catch (final Exception ex) {
129 // Obviously, the message is not a proper pattern for MessageFormat.
130 }
131 try {
132 if (MSG_PATTERN.matcher(msgPattern).find()) {
133 return new StringFormattedMessage(msgPattern, args);
134 }
135 } catch (final Exception ex) {
136 // Also not properly formatted.
137 }
138 return new ParameterizedMessage(msgPattern, args, throwable);
139 }
140
141 @Override
142 public boolean equals(final Object o) {
143 if (this == o) {
144 return true;
145 }
146 if (o == null || getClass() != o.getClass()) {
147 return false;
148 }
149
150 final FormattedMessage that = (FormattedMessage) o;
151
152 if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
153 return false;
154 }
155 if (!Arrays.equals(stringArgs, that.stringArgs)) {
156 return false;
157 }
158
159 return true;
160 }
161
162 @Override
163 public int hashCode() {
164 int result = messagePattern != null ? messagePattern.hashCode() : 0;
165 result = HASHVAL * result + (stringArgs != null ? Arrays.hashCode(stringArgs) : 0);
166 return result;
167 }
168
169
170 @Override
171 public String toString() {
172 return "FormattedMessage[messagePattern=" + messagePattern + ", args=" +
173 Arrays.toString(argArray) + "]";
174 }
175
176 private void writeObject(final ObjectOutputStream out) throws IOException {
177 out.defaultWriteObject();
178 getFormattedMessage();
179 out.writeUTF(formattedMessage);
180 out.writeUTF(messagePattern);
181 out.writeInt(argArray.length);
182 stringArgs = new String[argArray.length];
183 int i = 0;
184 for (final Object obj : argArray) {
185 stringArgs[i] = obj.toString();
186 ++i;
187 }
188 }
189
190 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
191 in.defaultReadObject();
192 formattedMessage = in.readUTF();
193 messagePattern = in.readUTF();
194 final int length = in.readInt();
195 stringArgs = new String[length];
196 for (int i = 0; i < length; ++i) {
197 stringArgs[i] = in.readUTF();
198 }
199 }
200
201 /**
202 * Always returns null.
203 *
204 * @return null
205 */
206 public Throwable getThrowable() {
207 if (throwable != null) {
208 return throwable;
209 }
210 if (message == null) {
211 message = getMessage(messagePattern, argArray, throwable);
212 }
213 return message.getThrowable();
214 }
215 }