Coverage Report - com.puppycrawl.tools.checkstyle.api.LocalizedMessage
 
Classes in this File Line Coverage Branch Coverage Complexity
LocalizedMessage
70%
51/72
53%
16/30
2.588
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 package com.puppycrawl.tools.checkstyle.api;
 20  
 
 21  
 import java.io.Serializable;
 22  
 import java.text.MessageFormat;
 23  
 import java.util.Arrays;
 24  
 import java.util.Collections;
 25  
 import java.util.HashMap;
 26  
 import java.util.Locale;
 27  
 import java.util.Map;
 28  
 import java.util.MissingResourceException;
 29  
 import java.util.ResourceBundle;
 30  
 
 31  
 
 32  
 /**
 33  
  * Represents a message that can be localised. The translations come from
 34  
  * message.properties files. The underlying implementation uses
 35  
  * java.text.MessageFormat.
 36  
  *
 37  
  * @author Oliver Burn
 38  
  * @author lkuehne
 39  
  * @version 1.0
 40  
  */
 41  25340
 public final class LocalizedMessage
 42  
     implements Comparable<LocalizedMessage>, Serializable
 43  
 {
 44  
     /** Required for serialization. */
 45  
     private static final long serialVersionUID = 5675176836184862150L;
 46  
 
 47  
     /** hash function multiplicand */
 48  
     private static final int HASH_MULT = 29;
 49  
 
 50  
     /** the locale to localise messages to **/
 51  1
     private static Locale sLocale = Locale.getDefault();
 52  
 
 53  
     /**
 54  
      * A cache that maps bundle names to RessourceBundles.
 55  
      * Avoids repetitive calls to ResourceBundle.getBundle().
 56  
      * TODO: The cache should be cleared at some point.
 57  
      */
 58  1
     private static final Map<String, ResourceBundle> BUNDLE_CACHE =
 59  
         Collections.synchronizedMap(new HashMap<String, ResourceBundle>());
 60  
 
 61  
     /** the line number **/
 62  
     private final int mLineNo;
 63  
     /** the column number **/
 64  
     private final int mColNo;
 65  
 
 66  
     /** the severity level **/
 67  
     private final SeverityLevel mSeverityLevel;
 68  
 
 69  
     /** the id of the module generating the message. */
 70  
     private final String mModuleId;
 71  
 
 72  
     /** the default severity level if one is not specified */
 73  1
     private static final SeverityLevel DEFAULT_SEVERITY = SeverityLevel.ERROR;
 74  
 
 75  
     /** key for the message format **/
 76  
     private final String mKey;
 77  
 
 78  
     /** arguments for MessageFormat **/
 79  
     private final Object[] mArgs;
 80  
 
 81  
     /** name of the resource bundle to get messages from **/
 82  
     private final String mBundle;
 83  
 
 84  
     /** class of the source for this LocalizedMessage */
 85  
     private final Class<?> mSourceClass;
 86  
 
 87  
     /** a custom message overriding the default message from the bundle. */
 88  
     private final String mCustomMessage;
 89  
 
 90  
     @Override
 91  
     public boolean equals(Object aObject)
 92  
     {
 93  0
         if (this == aObject) {
 94  0
             return true;
 95  
         }
 96  0
         if (!(aObject instanceof LocalizedMessage)) {
 97  0
             return false;
 98  
         }
 99  
 
 100  0
         final LocalizedMessage localizedMessage = (LocalizedMessage) aObject;
 101  
 
 102  0
         if (mColNo != localizedMessage.mColNo) {
 103  0
             return false;
 104  
         }
 105  0
         if (mLineNo != localizedMessage.mLineNo) {
 106  0
             return false;
 107  
         }
 108  0
         if (!mKey.equals(localizedMessage.mKey)) {
 109  0
             return false;
 110  
         }
 111  
 
 112  0
         if (!Arrays.equals(mArgs, localizedMessage.mArgs)) {
 113  0
             return false;
 114  
         }
 115  
         // ignoring mBundle for perf reasons.
 116  
 
 117  
         // we currently never load the same error from different bundles.
 118  
 
 119  0
         return true;
 120  
     }
 121  
 
 122  
     @Override
 123  
     public int hashCode()
 124  
     {
 125  
         int result;
 126  0
         result = mLineNo;
 127  0
         result = HASH_MULT * result + mColNo;
 128  0
         result = HASH_MULT * result + mKey.hashCode();
 129  0
         for (final Object element : mArgs) {
 130  0
             result = HASH_MULT * result + element.hashCode();
 131  
         }
 132  0
         return result;
 133  
     }
 134  
 
 135  
     /**
 136  
      * Creates a new <code>LocalizedMessage</code> instance.
 137  
      *
 138  
      * @param aLineNo line number associated with the message
 139  
      * @param aColNo column number associated with the message
 140  
      * @param aBundle resource bundle name
 141  
      * @param aKey the key to locate the translation
 142  
      * @param aArgs arguments for the translation
 143  
      * @param aSeverityLevel severity level for the message
 144  
      * @param aModuleId the id of the module the message is associated with
 145  
      * @param aSourceClass the Class that is the source of the message
 146  
      * @param aCustomMessage optional custom message overriding the default
 147  
      */
 148  
     public LocalizedMessage(int aLineNo,
 149  
                             int aColNo,
 150  
                             String aBundle,
 151  
                             String aKey,
 152  
                             Object[] aArgs,
 153  
                             SeverityLevel aSeverityLevel,
 154  
                             String aModuleId,
 155  
                             Class<?> aSourceClass,
 156  
                             String aCustomMessage)
 157  3446
     {
 158  3446
         mLineNo = aLineNo;
 159  3446
         mColNo = aColNo;
 160  3446
         mKey = aKey;
 161  3446
         mArgs = (null == aArgs) ? null : aArgs.clone();
 162  3446
         mBundle = aBundle;
 163  3446
         mSeverityLevel = aSeverityLevel;
 164  3446
         mModuleId = aModuleId;
 165  3446
         mSourceClass = aSourceClass;
 166  3446
         mCustomMessage = aCustomMessage;
 167  3446
     }
 168  
 
 169  
     /**
 170  
      * Creates a new <code>LocalizedMessage</code> instance.
 171  
      *
 172  
      * @param aLineNo line number associated with the message
 173  
      * @param aColNo column number associated with the message
 174  
      * @param aBundle resource bundle name
 175  
      * @param aKey the key to locate the translation
 176  
      * @param aArgs arguments for the translation
 177  
      * @param aModuleId the id of the module the message is associated with
 178  
      * @param aSourceClass the Class that is the source of the message
 179  
      * @param aCustomMessage optional custom message overriding the default
 180  
      */
 181  
     public LocalizedMessage(int aLineNo,
 182  
                             int aColNo,
 183  
                             String aBundle,
 184  
                             String aKey,
 185  
                             Object[] aArgs,
 186  
                             String aModuleId,
 187  
                             Class<?> aSourceClass,
 188  
                             String aCustomMessage)
 189  
     {
 190  10
         this(aLineNo,
 191  
              aColNo,
 192  
              aBundle,
 193  
              aKey,
 194  
              aArgs,
 195  
              DEFAULT_SEVERITY,
 196  
              aModuleId,
 197  
              aSourceClass,
 198  
              aCustomMessage);
 199  10
     }
 200  
 
 201  
     /**
 202  
      * Creates a new <code>LocalizedMessage</code> instance.
 203  
      *
 204  
      * @param aLineNo line number associated with the message
 205  
      * @param aBundle resource bundle name
 206  
      * @param aKey the key to locate the translation
 207  
      * @param aArgs arguments for the translation
 208  
      * @param aSeverityLevel severity level for the message
 209  
      * @param aModuleId the id of the module the message is associated with
 210  
      * @param aSourceClass the source class for the message
 211  
      * @param aCustomMessage optional custom message overriding the default
 212  
      */
 213  
     public LocalizedMessage(int aLineNo,
 214  
                             String aBundle,
 215  
                             String aKey,
 216  
                             Object[] aArgs,
 217  
                             SeverityLevel aSeverityLevel,
 218  
                             String aModuleId,
 219  
                             Class<?> aSourceClass,
 220  
                             String aCustomMessage)
 221  
     {
 222  734
         this(aLineNo, 0, aBundle, aKey, aArgs, aSeverityLevel, aModuleId,
 223  
                 aSourceClass, aCustomMessage);
 224  734
     }
 225  
 
 226  
     /**
 227  
      * Creates a new <code>LocalizedMessage</code> instance. The column number
 228  
      * defaults to 0.
 229  
      *
 230  
      * @param aLineNo line number associated with the message
 231  
      * @param aBundle name of a resource bundle that contains error messages
 232  
      * @param aKey the key to locate the translation
 233  
      * @param aArgs arguments for the translation
 234  
      * @param aModuleId the id of the module the message is associated with
 235  
      * @param aSourceClass the name of the source for the message
 236  
      * @param aCustomMessage optional custom message overriding the default
 237  
      */
 238  
     public LocalizedMessage(
 239  
         int aLineNo,
 240  
         String aBundle,
 241  
         String aKey,
 242  
         Object[] aArgs,
 243  
         String aModuleId,
 244  
         Class<?> aSourceClass,
 245  
         String aCustomMessage)
 246  
     {
 247  1
         this(aLineNo, 0, aBundle, aKey, aArgs, DEFAULT_SEVERITY, aModuleId,
 248  
                 aSourceClass, aCustomMessage);
 249  1
     }
 250  
 
 251  
     /** @return the translated message **/
 252  
     public String getMessage()
 253  
     {
 254  
 
 255  3667
         final String customMessage = getCustomMessage();
 256  3666
         if (customMessage != null) {
 257  5
             return customMessage;
 258  
         }
 259  
 
 260  
         try {
 261  
             // Important to use the default class loader, and not the one in
 262  
             // the GlobalProperties object. This is because the class loader in
 263  
             // the GlobalProperties is specified by the user for resolving
 264  
             // custom classes.
 265  3661
             final ResourceBundle bundle = getBundle(mBundle);
 266  3660
             final String pattern = bundle.getString(mKey);
 267  3624
             return MessageFormat.format(pattern, mArgs);
 268  
         }
 269  37
         catch (final MissingResourceException ex) {
 270  
             // If the Check author didn't provide i18n resource bundles
 271  
             // and logs error messages directly, this will return
 272  
             // the author's original message
 273  37
             return MessageFormat.format(mKey, mArgs);
 274  
         }
 275  
     }
 276  
 
 277  
     /**
 278  
      * Returns the formatted custom message if one is configured.
 279  
      * @return the formatted custom message or <code>null</code>
 280  
      *          if there is no custom message
 281  
      */
 282  
     private String getCustomMessage()
 283  
     {
 284  
 
 285  3667
         if (mCustomMessage == null) {
 286  3661
             return null;
 287  
         }
 288  
 
 289  6
         return MessageFormat.format(mCustomMessage, mArgs);
 290  
     }
 291  
 
 292  
     /**
 293  
      * Find a ResourceBundle for a given bundle name. Uses the classloader
 294  
      * of the class emitting this message, to be sure to get the correct
 295  
      * bundle.
 296  
      * @param aBundleName the bundle name
 297  
      * @return a ResourceBundle
 298  
      */
 299  
     private ResourceBundle getBundle(String aBundleName)
 300  
     {
 301  3661
         synchronized (BUNDLE_CACHE) {
 302  3661
             ResourceBundle bundle = BUNDLE_CACHE
 303  
                     .get(aBundleName);
 304  3661
             if (bundle == null) {
 305  18
                 bundle = ResourceBundle.getBundle(aBundleName, sLocale,
 306  
                         mSourceClass.getClassLoader());
 307  17
                 BUNDLE_CACHE.put(aBundleName, bundle);
 308  
             }
 309  3660
             return bundle;
 310  1
         }
 311  
     }
 312  
 
 313  
     /** @return the line number **/
 314  
     public int getLineNo()
 315  
     {
 316  103265
         return mLineNo;
 317  
     }
 318  
 
 319  
     /** @return the column number **/
 320  
     public int getColumnNo()
 321  
     {
 322  10548
         return mColNo;
 323  
     }
 324  
 
 325  
     /** @return the severity level **/
 326  
     public SeverityLevel getSeverityLevel()
 327  
     {
 328  6780
         return mSeverityLevel;
 329  
     }
 330  
 
 331  
     /** @return the module identifier. */
 332  
     public String getModuleId()
 333  
     {
 334  0
         return mModuleId;
 335  
     }
 336  
 
 337  
     /**
 338  
      * Returns the message key to locate the translation, can also be used
 339  
      * in IDE plugins to map error messages to corrective actions.
 340  
      *
 341  
      * @return the message key
 342  
      */
 343  
     public String getKey()
 344  
     {
 345  1
         return mKey;
 346  
     }
 347  
 
 348  
     /** @return the name of the source for this LocalizedMessage */
 349  
     public String getSourceName()
 350  
     {
 351  240
         return mSourceClass.getName();
 352  
     }
 353  
 
 354  
     /** @param aLocale the locale to use for localization **/
 355  
     public static void setLocale(Locale aLocale)
 356  
     {
 357  593
         sLocale = aLocale;
 358  593
     }
 359  
 
 360  
     ////////////////////////////////////////////////////////////////////////////
 361  
     // Interface Comparable methods
 362  
     ////////////////////////////////////////////////////////////////////////////
 363  
 
 364  
     /** {@inheritDoc} */
 365  
     public int compareTo(LocalizedMessage aOther)
 366  
     {
 367  25340
         if (getLineNo() == aOther.getLineNo()) {
 368  1203
             if (getColumnNo() == aOther.getColumnNo()) {
 369  139
                 return getMessage().compareTo(aOther.getMessage());
 370  
             }
 371  1064
             return (getColumnNo() < aOther.getColumnNo()) ? -1 : 1;
 372  
         }
 373  
 
 374  24137
         return (getLineNo() < aOther.getLineNo()) ? -1 : 1;
 375  
     }
 376  
 }