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
018package org.apache.logging.log4j.message;
019
020import java.io.IOException;
021import java.io.ObjectInputStream;
022import java.io.ObjectOutputStream;
023import java.text.MessageFormat;
024import java.util.Arrays;
025import java.util.IllegalFormatException;
026import java.util.Locale;
027
028import org.apache.logging.log4j.Logger;
029import org.apache.logging.log4j.status.StatusLogger;
030
031/**
032 * Handles messages that consist of a format string conforming to java.text.MessageFormat.
033 *
034 * @serial In version 2.1, due to a bug in the serialization format, the serialization format was changed along with
035 * its {@code serialVersionUID} value.
036 */
037public class MessageFormatMessage implements Message {
038
039    private static final Logger LOGGER = StatusLogger.getLogger();
040
041    private static final long serialVersionUID = 1L;
042
043    private static final int HASHVAL = 31;
044
045    private String messagePattern;
046    private transient Object[] parameters;
047    private String[] serializedParameters;
048    private transient String formattedMessage;
049    private transient Throwable throwable;
050    private final Locale locale;
051
052    /**
053     * Constructs a message.
054     * 
055     * @param locale the locale for this message format
056     * @param messagePattern the pattern for this message format
057     * @param parameters The objects to format
058     * @since 2.6
059     */
060    public MessageFormatMessage(final Locale locale, final String messagePattern, final Object... parameters) {
061        this.locale = locale;
062        this.messagePattern = messagePattern;
063        this.parameters = parameters;
064        final int length = parameters == null ? 0 : parameters.length;
065        if (length > 0 && parameters[length - 1] instanceof Throwable) {
066            this.throwable = (Throwable) parameters[length - 1];
067        }
068    }
069
070    /**
071     * Constructs a message.
072     * 
073     * @param messagePattern the pattern for this message format
074     * @param parameters The objects to format
075     */
076    public MessageFormatMessage(final String messagePattern, final Object... parameters) {
077        this(Locale.getDefault(Locale.Category.FORMAT), messagePattern, parameters);
078    }
079
080    /**
081     * Returns the formatted message.
082     * @return the formatted message.
083     */
084    @Override
085    public String getFormattedMessage() {
086        if (formattedMessage == null) {
087            formattedMessage = formatMessage(messagePattern, parameters);
088        }
089        return formattedMessage;
090    }
091
092    /**
093     * Returns the message pattern.
094     * @return the message pattern.
095     */
096    @Override
097    public String getFormat() {
098        return messagePattern;
099    }
100
101    /**
102     * Returns the message parameters.
103     * @return the message parameters.
104     */
105    @Override
106    public Object[] getParameters() {
107        if (parameters != null) {
108            return parameters;
109        }
110        return serializedParameters;
111    }
112
113    protected String formatMessage(final String msgPattern, final Object... args) {
114        try {
115            final MessageFormat temp = new MessageFormat(msgPattern, locale);
116            return temp.format(args);
117        } catch (final IllegalFormatException ife) {
118            LOGGER.error("Unable to format msg: " + msgPattern, ife);
119            return msgPattern;
120        }
121    }
122
123    @Override
124    public boolean equals(final Object o) {
125        if (this == o) {
126            return true;
127        }
128        if (o == null || getClass() != o.getClass()) {
129            return false;
130        }
131
132        final MessageFormatMessage that = (MessageFormatMessage) o;
133
134        if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
135            return false;
136        }
137        return Arrays.equals(serializedParameters, that.serializedParameters);
138    }
139
140    @Override
141    public int hashCode() {
142        int result = messagePattern != null ? messagePattern.hashCode() : 0;
143        result = HASHVAL * result + (serializedParameters != null ? Arrays.hashCode(serializedParameters) : 0);
144        return result;
145    }
146
147
148    @Override
149    public String toString() {
150        return getFormattedMessage();
151    }
152
153    private void writeObject(final ObjectOutputStream out) throws IOException {
154        getFormattedMessage();
155        out.writeUTF(formattedMessage);
156        out.writeUTF(messagePattern);
157        final int length = parameters == null ? 0 : parameters.length;
158        out.writeInt(length);
159        serializedParameters = new String[length];
160        if (length > 0) {
161            for (int i = 0; i < length; i++) {
162                serializedParameters[i] = String.valueOf(parameters[i]);
163                out.writeUTF(serializedParameters[i]);
164            }
165        }
166    }
167
168    private void readObject(final ObjectInputStream in) throws IOException {
169        parameters = null;
170        throwable = null;
171        formattedMessage = in.readUTF();
172        messagePattern = in.readUTF();
173        final int length = in.readInt();
174        serializedParameters = new String[length];
175        for (int i = 0; i < length; ++i) {
176            serializedParameters[i] = in.readUTF();
177        }
178    }
179
180    /**
181     * Return the throwable passed to the Message.
182     *
183     * @return the Throwable.
184     */
185    @Override
186    public Throwable getThrowable() {
187        return throwable;
188    }
189}