1 /*
2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util.logging;
27 import java.lang.reflect.Module;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33 import java.util.ResourceBundle;
34
35 /**
36 * The Level class defines a set of standard logging levels that
37 * can be used to control logging output. The logging Level objects
38 * are ordered and are specified by ordered integers. Enabling logging
39 * at a given level also enables logging at all higher levels.
40 * <p>
41 * Clients should normally use the predefined Level constants such
42 * as Level.SEVERE.
43 * <p>
44 * The levels in descending order are:
45 * <ul>
46 * <li>SEVERE (highest value)
47 * <li>WARNING
48 * <li>INFO
49 * <li>CONFIG
50 * <li>FINE
51 * <li>FINER
52 * <li>FINEST (lowest value)
53 * </ul>
250 * If no localization information is available, the
251 * non-localized name is returned.
252 *
253 * @return localized name
254 */
255 public String getLocalizedName() {
256 return getLocalizedLevelName();
257 }
258
259 // package-private getLevelName() is used by the implementation
260 // instead of getName() to avoid calling the subclass's version
261 final String getLevelName() {
262 return this.name;
263 }
264
265 private String computeLocalizedLevelName(Locale newLocale) {
266 // Resource bundle should be loaded from the defining module
267 // or its defining class loader, if it's unnamed module,
268 // of this Level instance that can be a custom Level subclass;
269 Module module = this.getClass().getModule();
270 ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName, newLocale, module);
271
272 final String localizedName = rb.getString(name);
273 final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
274 if (!isDefaultBundle) return localizedName;
275
276 // This is a trick to determine whether the name has been translated
277 // or not. If it has not been translated, we need to use Locale.ROOT
278 // when calling toUpperCase().
279 final Locale rbLocale = rb.getLocale();
280 final Locale locale =
281 Locale.ROOT.equals(rbLocale)
282 || name.equals(localizedName.toUpperCase(Locale.ROOT))
283 ? Locale.ROOT : rbLocale;
284
285 // ALL CAPS in a resource bundle's message indicates no translation
286 // needed per Oracle translation guideline. To workaround this
287 // in Oracle JDK implementation, convert the localized level name
288 // to uppercase for compatibility reason.
289 return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale);
290 }
333 cachedLocale = newLocale;
334 return localizedLevelName;
335 }
336
337 // Returns a mirrored Level object that matches the given name as
338 // specified in the Level.parse method. Returns null if not found.
339 //
340 // It returns the same Level object as the one returned by Level.parse
341 // method if the given name is a non-localized name or integer.
342 //
343 // If the name is a localized name, findLevel and parse method may
344 // return a different level value if there is a custom Level subclass
345 // that overrides Level.getLocalizedName() to return a different string
346 // than what's returned by the default implementation.
347 //
348 static Level findLevel(String name) {
349 if (name == null) {
350 throw new NullPointerException();
351 }
352
353 KnownLevel level;
354
355 // Look for a known Level with the given non-localized name.
356 level = KnownLevel.findByName(name);
357 if (level != null) {
358 return level.mirroredLevel;
359 }
360
361 // Now, check if the given name is an integer. If so,
362 // first look for a Level with the given value and then
363 // if necessary create one.
364 try {
365 int x = Integer.parseInt(name);
366 level = KnownLevel.findByValue(x);
367 if (level == null) {
368 // add new Level
369 Level levelObject = new Level(name, x);
370 level = KnownLevel.findByValue(x);
371 }
372 return level.mirroredLevel;
373 } catch (NumberFormatException ex) {
374 // Not an integer.
375 // Drop through.
376 }
377
378 level = KnownLevel.findByLocalizedLevelName(name);
379 if (level != null) {
380 return level.mirroredLevel;
381 }
382
383 return null;
384 }
385
386 /**
387 * Returns a string representation of this Level.
388 *
389 * @return the non-localized name of the Level, for example "INFO".
390 */
391 @Override
392 public final String toString() {
393 return name;
394 }
395
396 /**
397 * Get the integer value for this level. This integer value
398 * can be used for efficient ordering comparisons between
399 * Level objects.
400 * @return the integer value for this level.
401 */
402 public final int intValue() {
403 return value;
404 }
405
406 private static final long serialVersionUID = -8176160795706313070L;
407
408 // Serialization magic to prevent "doppelgangers".
409 // This is a performance optimization.
410 private Object readResolve() {
411 KnownLevel o = KnownLevel.matches(this);
412 if (o != null) {
413 return o.levelObject;
414 }
415
416 // Woops. Whoever sent us this object knows
417 // about a new log level. Add it to our list.
418 Level level = new Level(this.name, this.value, this.resourceBundleName);
419 return level;
420 }
421
422 /**
423 * Parse a level name string into a Level.
424 * <p>
425 * The argument string may consist of either a level name
426 * or an integer value.
427 * <p>
428 * For example:
429 * <ul>
430 * <li> "SEVERE"
431 * <li> "1000"
432 * </ul>
433 *
434 * @param name string to be parsed
435 * @throws NullPointerException if the name is null
436 * @throws IllegalArgumentException if the value is not valid.
437 * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE>
438 * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names.
439 * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>,
440 * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with
441 * appropriate package access, or new levels defined or created
442 * by subclasses.
443 *
444 * @return The parsed value. Passing an integer that corresponds to a known name
445 * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>).
446 * Passing an integer that does not (e.g., 1) will return a new level name
447 * initialized to that value.
448 */
449 public static synchronized Level parse(String name) throws IllegalArgumentException {
450 // Check that name is not null.
451 name.length();
452
453 KnownLevel level;
454
455 // Look for a known Level with the given non-localized name.
456 level = KnownLevel.findByName(name);
457 if (level != null) {
458 return level.levelObject;
459 }
460
461 // Now, check if the given name is an integer. If so,
462 // first look for a Level with the given value and then
463 // if necessary create one.
464 try {
465 int x = Integer.parseInt(name);
466 level = KnownLevel.findByValue(x);
467 if (level == null) {
468 // add new Level
469 Level levelObject = new Level(name, x);
470 level = KnownLevel.findByValue(x);
471 }
472 return level.levelObject;
473 } catch (NumberFormatException ex) {
474 // Not an integer.
475 // Drop through.
476 }
477
478 // Finally, look for a known level with the given localized name,
479 // in the current default locale.
480 // This is relatively expensive, but not excessively so.
481 level = KnownLevel.findByLocalizedLevelName(name);
482 if (level != null) {
483 return level.levelObject;
484 }
485
486 // OK, we've tried everything and failed
487 throw new IllegalArgumentException("Bad level \"" + name + "\"");
488 }
489
490 /**
491 * Compare two objects for value equality.
492 * @return true if and only if the two objects have the same level value.
493 */
494 @Override
495 public boolean equals(Object ox) {
496 try {
497 Level lx = (Level)ox;
498 return (lx.value == this.value);
499 } catch (Exception ex) {
500 return false;
501 }
502 }
503
513 // KnownLevel class maintains the global list of all known levels.
514 // The API allows multiple custom Level instances of the same name/value
515 // be created. This class provides convenient methods to find a level
516 // by a given name, by a given value, or by a given localized name.
517 //
518 // KnownLevel wraps the following Level objects:
519 // 1. levelObject: standard Level object or custom Level object
520 // 2. mirroredLevel: Level object representing the level specified in the
521 // logging configuration.
522 //
523 // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
524 // are non-final but the name and resource bundle name are parameters to
525 // the Level constructor. Use the mirroredLevel object instead of the
526 // levelObject to prevent the logging framework to execute foreign code
527 // implemented by untrusted Level subclass.
528 //
529 // Implementation Notes:
530 // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
531 // were final, the following KnownLevel implementation can be removed.
532 // Future API change should take this into consideration.
533 static final class KnownLevel {
534 private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
535 private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
536 final Level levelObject; // instance of Level class or Level subclass
537 final Level mirroredLevel; // mirror of the custom Level
538 KnownLevel(Level l) {
539 this.levelObject = l;
540 if (l.getClass() == Level.class) {
541 this.mirroredLevel = l;
542 } else {
543 // this mirrored level object is hidden
544 this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName, false);
545 }
546 }
547
548 static synchronized void add(Level l) {
549 // the mirroredLevel object is always added to the list
550 // before the custom Level instance
551 KnownLevel o = new KnownLevel(l);
552 List<KnownLevel> list = nameToLevels.get(l.name);
553 if (list == null) {
554 list = new ArrayList<>();
555 nameToLevels.put(l.name, list);
556 }
557 list.add(o);
558
559 list = intToLevels.get(l.value);
560 if (list == null) {
561 list = new ArrayList<>();
562 intToLevels.put(l.value, list);
563 }
564 list.add(o);
565 }
566
567 // Returns a KnownLevel with the given non-localized name.
568 static synchronized KnownLevel findByName(String name) {
569 List<KnownLevel> list = nameToLevels.get(name);
570 if (list != null) {
571 return list.get(0);
572 }
573 return null;
574 }
575
576 // Returns a KnownLevel with the given value.
577 static synchronized KnownLevel findByValue(int value) {
578 List<KnownLevel> list = intToLevels.get(value);
579 if (list != null) {
580 return list.get(0);
581 }
582 return null;
583 }
584
585 // Returns a KnownLevel with the given localized name matching
586 // by calling the Level.getLocalizedLevelName() method (i.e. found
587 // from the resourceBundle associated with the Level object).
588 // This method does not call Level.getLocalizedName() that may
589 // be overridden in a subclass implementation
590 static synchronized KnownLevel findByLocalizedLevelName(String name) {
591 for (List<KnownLevel> levels : nameToLevels.values()) {
592 for (KnownLevel l : levels) {
593 String lname = l.levelObject.getLocalizedLevelName();
594 if (name.equals(lname)) {
595 return l;
596 }
597 }
598 }
599 return null;
600 }
601
602 static synchronized KnownLevel matches(Level l) {
603 List<KnownLevel> list = nameToLevels.get(l.name);
604 if (list != null) {
605 for (KnownLevel level : list) {
606 Level other = level.mirroredLevel;
607 if (l.value == other.value &&
608 (l.resourceBundleName == other.resourceBundleName ||
609 (l.resourceBundleName != null &&
610 l.resourceBundleName.equals(other.resourceBundleName)))) {
611 return level;
612 }
613 }
614 }
615 return null;
616 }
617 }
618
619 }
|
1 /*
2 * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util.logging;
27 import java.lang.ref.Reference;
28 import java.lang.ref.ReferenceQueue;
29 import java.lang.ref.WeakReference;
30 import java.lang.reflect.Module;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Optional;
38 import java.util.ResourceBundle;
39 import java.util.function.Function;
40 import java.util.stream.Stream;
41
42 /**
43 * The Level class defines a set of standard logging levels that
44 * can be used to control logging output. The logging Level objects
45 * are ordered and are specified by ordered integers. Enabling logging
46 * at a given level also enables logging at all higher levels.
47 * <p>
48 * Clients should normally use the predefined Level constants such
49 * as Level.SEVERE.
50 * <p>
51 * The levels in descending order are:
52 * <ul>
53 * <li>SEVERE (highest value)
54 * <li>WARNING
55 * <li>INFO
56 * <li>CONFIG
57 * <li>FINE
58 * <li>FINER
59 * <li>FINEST (lowest value)
60 * </ul>
257 * If no localization information is available, the
258 * non-localized name is returned.
259 *
260 * @return localized name
261 */
262 public String getLocalizedName() {
263 return getLocalizedLevelName();
264 }
265
266 // package-private getLevelName() is used by the implementation
267 // instead of getName() to avoid calling the subclass's version
268 final String getLevelName() {
269 return this.name;
270 }
271
272 private String computeLocalizedLevelName(Locale newLocale) {
273 // Resource bundle should be loaded from the defining module
274 // or its defining class loader, if it's unnamed module,
275 // of this Level instance that can be a custom Level subclass;
276 Module module = this.getClass().getModule();
277 ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName,
278 newLocale, module);
279
280 final String localizedName = rb.getString(name);
281 final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
282 if (!isDefaultBundle) return localizedName;
283
284 // This is a trick to determine whether the name has been translated
285 // or not. If it has not been translated, we need to use Locale.ROOT
286 // when calling toUpperCase().
287 final Locale rbLocale = rb.getLocale();
288 final Locale locale =
289 Locale.ROOT.equals(rbLocale)
290 || name.equals(localizedName.toUpperCase(Locale.ROOT))
291 ? Locale.ROOT : rbLocale;
292
293 // ALL CAPS in a resource bundle's message indicates no translation
294 // needed per Oracle translation guideline. To workaround this
295 // in Oracle JDK implementation, convert the localized level name
296 // to uppercase for compatibility reason.
297 return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale);
298 }
341 cachedLocale = newLocale;
342 return localizedLevelName;
343 }
344
345 // Returns a mirrored Level object that matches the given name as
346 // specified in the Level.parse method. Returns null if not found.
347 //
348 // It returns the same Level object as the one returned by Level.parse
349 // method if the given name is a non-localized name or integer.
350 //
351 // If the name is a localized name, findLevel and parse method may
352 // return a different level value if there is a custom Level subclass
353 // that overrides Level.getLocalizedName() to return a different string
354 // than what's returned by the default implementation.
355 //
356 static Level findLevel(String name) {
357 if (name == null) {
358 throw new NullPointerException();
359 }
360
361 Optional<Level> level;
362
363 // Look for a known Level with the given non-localized name.
364 level = KnownLevel.findByName(name, KnownLevel::mirrored);
365 if (level.isPresent()) {
366 return level.get();
367 }
368
369 // Now, check if the given name is an integer. If so,
370 // first look for a Level with the given value and then
371 // if necessary create one.
372 try {
373 int x = Integer.parseInt(name);
374 level = KnownLevel.findByValue(x, KnownLevel::mirrored);
375 if (!level.isPresent()) {
376 // add new Level
377 Level levelObject = new Level(name, x);
378 return KnownLevel.findByValue(x, KnownLevel::mirrored).get();
379 }
380 } catch (NumberFormatException ex) {
381 // Not an integer.
382 // Drop through.
383 }
384
385 level = KnownLevel.findByLocalizedLevelName(name,
386 KnownLevel::mirrored);
387 if (level.isPresent()) {
388 return level.get();
389 }
390
391 return null;
392 }
393
394 /**
395 * Returns a string representation of this Level.
396 *
397 * @return the non-localized name of the Level, for example "INFO".
398 */
399 @Override
400 public final String toString() {
401 return name;
402 }
403
404 /**
405 * Get the integer value for this level. This integer value
406 * can be used for efficient ordering comparisons between
407 * Level objects.
408 * @return the integer value for this level.
409 */
410 public final int intValue() {
411 return value;
412 }
413
414 private static final long serialVersionUID = -8176160795706313070L;
415
416 // Serialization magic to prevent "doppelgangers".
417 // This is a performance optimization.
418 private Object readResolve() {
419 Optional<Level> level = KnownLevel.matches(this);
420 if (level.isPresent()) {
421 return level.get();
422 }
423 // Woops. Whoever sent us this object knows
424 // about a new log level. Add it to our list.
425 return new Level(this.name, this.value, this.resourceBundleName);
426 }
427
428 /**
429 * Parse a level name string into a Level.
430 * <p>
431 * The argument string may consist of either a level name
432 * or an integer value.
433 * <p>
434 * For example:
435 * <ul>
436 * <li> "SEVERE"
437 * <li> "1000"
438 * </ul>
439 *
440 * @param name string to be parsed
441 * @throws NullPointerException if the name is null
442 * @throws IllegalArgumentException if the value is not valid.
443 * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE>
444 * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names.
445 * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>,
446 * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with
447 * appropriate package access, or new levels defined or created
448 * by subclasses.
449 *
450 * @return The parsed value. Passing an integer that corresponds to a known name
451 * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>).
452 * Passing an integer that does not (e.g., 1) will return a new level name
453 * initialized to that value.
454 */
455 public static synchronized Level parse(String name) throws IllegalArgumentException {
456 // Check that name is not null.
457 name.length();
458
459 Optional<Level> level;
460
461 // Look for a known Level with the given non-localized name.
462 level = KnownLevel.findByName(name, KnownLevel::referent);
463 if (level.isPresent()) {
464 return level.get();
465 }
466
467 // Now, check if the given name is an integer. If so,
468 // first look for a Level with the given value and then
469 // if necessary create one.
470 try {
471 int x = Integer.parseInt(name);
472 level = KnownLevel.findByValue(x, KnownLevel::referent);
473 if (level.isPresent()) {
474 return level.get();
475 }
476 // add new Level.
477 Level levelObject = new Level(name, x);
478 return KnownLevel.findByValue(x, KnownLevel::referent).get();
479 } catch (NumberFormatException ex) {
480 // Not an integer.
481 // Drop through.
482 }
483
484 // Finally, look for a known level with the given localized name,
485 // in the current default locale.
486 // This is relatively expensive, but not excessively so.
487 level = KnownLevel.findByLocalizedLevelName(name, KnownLevel::referent);
488 if (level .isPresent()) {
489 return level.get();
490 }
491
492 // OK, we've tried everything and failed
493 throw new IllegalArgumentException("Bad level \"" + name + "\"");
494 }
495
496 /**
497 * Compare two objects for value equality.
498 * @return true if and only if the two objects have the same level value.
499 */
500 @Override
501 public boolean equals(Object ox) {
502 try {
503 Level lx = (Level)ox;
504 return (lx.value == this.value);
505 } catch (Exception ex) {
506 return false;
507 }
508 }
509
519 // KnownLevel class maintains the global list of all known levels.
520 // The API allows multiple custom Level instances of the same name/value
521 // be created. This class provides convenient methods to find a level
522 // by a given name, by a given value, or by a given localized name.
523 //
524 // KnownLevel wraps the following Level objects:
525 // 1. levelObject: standard Level object or custom Level object
526 // 2. mirroredLevel: Level object representing the level specified in the
527 // logging configuration.
528 //
529 // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
530 // are non-final but the name and resource bundle name are parameters to
531 // the Level constructor. Use the mirroredLevel object instead of the
532 // levelObject to prevent the logging framework to execute foreign code
533 // implemented by untrusted Level subclass.
534 //
535 // Implementation Notes:
536 // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
537 // were final, the following KnownLevel implementation can be removed.
538 // Future API change should take this into consideration.
539 static final class KnownLevel extends WeakReference<Level> {
540 private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
541 private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
542 private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>();
543
544 final Level mirroredLevel; // mirror of the custom Level
545 KnownLevel(Level l) {
546 super(l, QUEUE);
547 if (l.getClass() == Level.class) {
548 this.mirroredLevel = l;
549 } else {
550 // this mirrored level object is hidden
551 this.mirroredLevel = new Level(l.name, l.value,
552 l.resourceBundleName, false);
553 }
554 }
555
556 Stream<Level> mirrored() {
557 return Stream.of(mirroredLevel);
558 }
559
560 Stream<Level> referent() {
561 final Level ref = get();
562 return ref == null ? Stream.empty() : Stream.of(ref);
563 }
564
565 private void remove() {
566 Optional.ofNullable(nameToLevels.get(mirroredLevel.name))
567 .ifPresent((x) -> x.remove(this));
568 Optional.ofNullable(intToLevels.get(mirroredLevel.value))
569 .ifPresent((x) -> x.remove(this));
570 }
571
572 // Remove all stale KnownLevel instances
573 static synchronized void purge() {
574 Reference<? extends Level> ref;
575 while ((ref = QUEUE.poll()) != null) {
576 if (ref instanceof KnownLevel) {
577 ((KnownLevel)ref).remove();
578 }
579 }
580 }
581
582 static synchronized void add(Level l) {
583 purge();
584 // the mirroredLevel object is always added to the list
585 // before the custom Level instance
586 KnownLevel o = new KnownLevel(l);
587 List<KnownLevel> list = nameToLevels.get(l.name);
588 if (list == null) {
589 list = new ArrayList<>();
590 nameToLevels.put(l.name, list);
591 }
592 list.add(o);
593
594 list = intToLevels.get(l.value);
595 if (list == null) {
596 list = new ArrayList<>();
597 intToLevels.put(l.value, list);
598 }
599 list.add(o);
600 }
601
602 // Returns a KnownLevel with the given non-localized name.
603 static synchronized Optional<Level> findByName(String name,
604 Function<KnownLevel, Stream<Level>> selector) {
605 purge();
606 return nameToLevels.getOrDefault(name, Collections.emptyList())
607 .stream()
608 .flatMap(selector)
609 .findFirst();
610 }
611
612 // Returns a KnownLevel with the given value.
613 static synchronized Optional<Level> findByValue(int value,
614 Function<KnownLevel, Stream<Level>> selector) {
615 purge();
616 return intToLevels.getOrDefault(value, Collections.emptyList())
617 .stream()
618 .flatMap(selector)
619 .findFirst();
620 }
621
622 // Returns a KnownLevel with the given localized name matching
623 // by calling the Level.getLocalizedLevelName() method (i.e. found
624 // from the resourceBundle associated with the Level object).
625 // This method does not call Level.getLocalizedName() that may
626 // be overridden in a subclass implementation
627 static synchronized Optional<Level> findByLocalizedLevelName(String name,
628 Function<KnownLevel, Stream<Level>> selector) {
629 purge();
630 return nameToLevels.values()
631 .stream()
632 .flatMap(List::stream)
633 .flatMap(selector)
634 .filter(lo -> name.equals(lo.getLocalizedLevelName()))
635 .findFirst();
636 }
637
638 static synchronized Optional<Level> matches(Level l) {
639 purge();
640 List<KnownLevel> list = nameToLevels.get(l.name);
641 if (list != null) {
642 for (KnownLevel ref : list) {
643 Level levelObject = ref.get();
644 if (levelObject == null) continue;
645 Level other = ref.mirroredLevel;
646 if (l.value == other.value &&
647 (l.resourceBundleName == other.resourceBundleName ||
648 (l.resourceBundleName != null &&
649 l.resourceBundleName.equals(other.resourceBundleName)))) {
650 return Optional.of(levelObject);
651 }
652 }
653 }
654 return Optional.empty();
655 }
656 }
657
658 }
|