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