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