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 java.io.IOException;
020    import java.io.ObjectInputStream;
021    import java.io.ObjectOutputStream;
022    import java.text.MessageFormat;
023    import java.util.Arrays;
024    import java.util.IllegalFormatException;
025    
026    import org.apache.logging.log4j.Logger;
027    import org.apache.logging.log4j.status.StatusLogger;
028    
029    /**
030     * Handles messages that consist of a format string conforming to java.text.MessageFormat.
031     */
032    public 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    }