View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.message;
    18
    19  import java.io.IOException;
    20  import java.io.ObjectInputStream;
    21  import java.io.ObjectOutputStream;
    22  import java.text.Format;
    23  import java.text.MessageFormat;
    24  import java.util.Arrays;
    25  import java.util.regex.Pattern;
    26
    27  /**
    28   * Handles messages that contain a format String. Dynamically determines if the format conforms to
    29   * MessageFormat or String.format and if not then uses ParameterizedMessage to format.
    30   */
    31  public class FormattedMessage implements Message {
    32
    33      private static final long serialVersionUID = -665975803997290697L;
    34      private static final int HASHVAL = 31;
    35      private static final String FORMAT_SPECIFIER = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
    36      private static final Pattern MSG_PATTERN = Pattern.compile(FORMAT_SPECIFIER);
    37
    38      private String messagePattern;
    39      private transient Object[] argArray;
    40      private String[] stringArgs;
    41      private transient String formattedMessage;
    42      private final Throwable throwable;
    43      private Message message;
    44
    45      public FormattedMessage(final String messagePattern, final Object[] arguments, final Throwable throwable) {
    46          this.messagePattern = messagePattern;
    47          this.argArray = arguments;
    48          this.throwable = throwable;
    49      }
    50
    51      public FormattedMessage(final String messagePattern, final Object[] arguments) {
    52          this(messagePattern, arguments, null);
    53      }
    54
    55      /**
    56       * Constructor with a pattern and a single parameter.
    57       * @param messagePattern The message pattern.
    58       * @param arg The parameter.
    59       */
    60      public FormattedMessage(final String messagePattern, final Object arg) {
    61          this(messagePattern, new Object[] {arg}, null);
    62      }
    63
    64      /**
    65       * Constructor with a pattern and two parameters.
    66       * @param messagePattern The message pattern.
    67       * @param arg1 The first parameter.
    68       * @param arg2 The second parameter.
    69       */
    70      public FormattedMessage(final String messagePattern, final Object arg1, final Object arg2) {
    71          this(messagePattern, new Object[]{arg1, arg2});
    72      }
    73
    74
    75      /**
    76       * Returns the formatted message.
    77       * @return the formatted message.
    78       */
    79      @Override
    80      public String getFormattedMessage() {
    81          if (formattedMessage == null) {
    82              if (message == null) {
    83                  message = getMessage(messagePattern, argArray, throwable);
    84              }
    85              formattedMessage = message.getFormattedMessage();
    86          }
    87          return formattedMessage;
    88      }
    89
    90      /**
    91       * Returns the message pattern.
    92       * @return the message pattern.
    93       */
    94      @Override
    95      public String getFormat() {
    96          return messagePattern;
    97      }
    98
    99      /**
    100      * Returns the message parameters.
    101      * @return the message parameters.
    102      */
    103     @Override
    104     public Object[] getParameters() {
    105         if (argArray != null) {
    106             return argArray;
    107         }
    108         return stringArgs;
    109     }
    110
    111     protected Message getMessage(final String msgPattern, final Object[] args, final Throwable throwable) {
    112         try {
    113             final MessageFormat format = new MessageFormat(msgPattern);
    114             final Format[] formats = format.getFormats();
    115             if (formats != null && formats.length > 0) {
    116                 return new MessageFormatMessage(msgPattern, args);
    117             }
    118         } catch (final Exception ignored) {
    119             // Obviously, the message is not a proper pattern for MessageFormat.
    120         }
    121         try {
    122             if (MSG_PATTERN.matcher(msgPattern).find()) {
    123                 return new StringFormattedMessage(msgPattern, args);
    124             }
    125         } catch (final Exception ignored) {
    126             // Also not properly formatted.
    127         }
    128         return new ParameterizedMessage(msgPattern, args, throwable);
    129     }
    130
    131     @Override
    132     public boolean equals(final Object o) {
    133         if (this == o) {
    134             return true;
    135         }
    136         if (o == null || getClass() != o.getClass()) {
    137             return false;
    138         }
    139
    140         final FormattedMessage that = (FormattedMessage) o;
    141
    142         if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
    143             return false;
    144         }
    145         if (!Arrays.equals(stringArgs, that.stringArgs)) {
    146             return false;
    147         }
    148
    149         return true;
    150     }
    151
    152     @Override
    153     public int hashCode() {
    154         int result = messagePattern != null ? messagePattern.hashCode() : 0;
    155         result = HASHVAL * result + (stringArgs != null ? Arrays.hashCode(stringArgs) : 0);
    156         return result;
    157     }
    158
    159
    160     @Override
    161     public String toString() {
    162         return "FormattedMessage[messagePattern=" + messagePattern + ", args=" +
    163             Arrays.toString(argArray) + ']';
    164     }
    165
    166     private void writeObject(final ObjectOutputStream out) throws IOException {
    167         out.defaultWriteObject();
    168         getFormattedMessage();
    169         out.writeUTF(formattedMessage);
    170         out.writeUTF(messagePattern);
    171         out.writeInt(argArray.length);
    172         stringArgs = new String[argArray.length];
    173         int i = 0;
    174         for (final Object obj : argArray) {
    175             stringArgs[i] = obj.toString();
    176             ++i;
    177         }
    178     }
    179
    180     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
    181         in.defaultReadObject();
    182         formattedMessage = in.readUTF();
    183         messagePattern = in.readUTF();
    184         final int length = in.readInt();
    185         stringArgs = new String[length];
    186         for (int i = 0; i < length; ++i) {
    187             stringArgs[i] = in.readUTF();
    188         }
    189     }
    190
    191     /**
    192      * Always returns null.
    193      *
    194      * @return null
    195      */
    196     @Override
    197     public Throwable getThrowable() {
    198         if (throwable != null) {
    199             return throwable;
    200         }
    201         if (message == null) {
    202             message = getMessage(messagePattern, argArray, null);
    203         }
    204         return message.getThrowable();
    205     }
    206 }