| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| LocalizedMessage |
|
| 2.588235294117647;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 | } |