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 }