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;
024import java.util.Locale;
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 {@link java.util.Formatter}.
031 */
032public class StringFormattedMessage 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    private final Locale locale;
046    
047   /**
048    * Constructs a message.
049    * 
050    * @param locale the locale for this message format
051    * @param messagePattern the pattern for this message format
052    * @param arguments The objects to format
053    * @since 2.6
054    */
055    public StringFormattedMessage(final Locale locale, final String messagePattern, final Object... arguments) {
056        this.locale = locale;
057        this.messagePattern = messagePattern;
058        this.argArray = arguments;
059        if (arguments != null && arguments.length > 0 && arguments[arguments.length - 1] instanceof Throwable) {
060            this.throwable = (Throwable) arguments[arguments.length - 1];
061        }
062    }
063
064    /**
065     * Constructs a message.
066     * 
067     * @param messagePattern the pattern for this message format
068     * @param arguments The objects to format
069     * @since 2.6
070     */
071    public StringFormattedMessage(final String messagePattern, final Object... arguments) {
072        this(Locale.getDefault(Locale.Category.FORMAT), messagePattern, arguments);
073    }
074
075    /**
076     * Returns the formatted message.
077     * @return the formatted message.
078     */
079    @Override
080    public String getFormattedMessage() {
081        if (formattedMessage == null) {
082            formattedMessage = formatMessage(messagePattern, argArray);
083        }
084        return formattedMessage;
085    }
086
087    /**
088     * Returns the message pattern.
089     * @return the message pattern.
090     */
091    @Override
092    public String getFormat() {
093        return messagePattern;
094    }
095
096    /**
097     * Returns the message parameters.
098     * @return the message parameters.
099     */
100    @Override
101    public Object[] getParameters() {
102        if (argArray != null) {
103            return argArray;
104        }
105        return stringArgs;
106    }
107
108    protected String formatMessage(final String msgPattern, final Object... args) {
109        try {
110            return String.format(locale, msgPattern, args);
111        } catch (final IllegalFormatException ife) {
112            LOGGER.error("Unable to format msg: " + msgPattern, ife);
113            return msgPattern;
114        }
115    }
116
117    @Override
118    public boolean equals(final Object o) {
119        if (this == o) {
120            return true;
121        }
122        if (o == null || getClass() != o.getClass()) {
123            return false;
124        }
125
126        final StringFormattedMessage that = (StringFormattedMessage) o;
127
128        if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
129            return false;
130        }
131
132        return Arrays.equals(stringArgs, that.stringArgs);
133    }
134
135    @Override
136    public int hashCode() {
137        int result = messagePattern != null ? messagePattern.hashCode() : 0;
138        result = HASHVAL * result + (stringArgs != null ? Arrays.hashCode(stringArgs) : 0);
139        return result;
140    }
141
142
143    @Override
144    public String toString() {
145        return getFormattedMessage();
146    }
147
148    private void writeObject(final ObjectOutputStream out) throws IOException {
149        out.defaultWriteObject();
150        getFormattedMessage();
151        out.writeUTF(formattedMessage);
152        out.writeUTF(messagePattern);
153        out.writeInt(argArray.length);
154        stringArgs = new String[argArray.length];
155        int i = 0;
156        for (final Object obj : argArray) {
157            final String string = String.valueOf(obj);
158            stringArgs[i] = string;
159            out.writeUTF(string);
160            ++i;
161        }
162    }
163
164    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
165        in.defaultReadObject();
166        formattedMessage = in.readUTF();
167        messagePattern = in.readUTF();
168        final int length = in.readInt();
169        stringArgs = new String[length];
170        for (int i = 0; i < length; ++i) {
171            stringArgs[i] = in.readUTF();
172        }
173    }
174
175    /**
176     * Return the throwable passed to the Message.
177     *
178     * @return the Throwable.
179     */
180    @Override
181    public Throwable getThrowable() {
182        return throwable;
183    }
184}