1 package org.unicode.cldr.util; 2 3 import static com.google.common.base.Preconditions.checkArgument; 4 import static com.google.common.base.Preconditions.checkNotNull; 5 import static com.google.common.collect.Comparators.lexicographical; 6 import static com.google.common.collect.ImmutableList.toImmutableList; 7 8 import java.util.Collection; 9 import java.util.Comparator; 10 import java.util.Set; 11 import java.util.stream.Stream; 12 13 import com.google.common.base.Joiner; 14 import com.google.common.base.Splitter; 15 import com.google.common.collect.ImmutableList; 16 17 public final class PreferredAndAllowedHour implements Comparable<PreferredAndAllowedHour> { 18 // DO NOT change enum item names, they are mapped directly from data values via "valueOf". 19 public enum HourStyle { 20 H, Hb(H), HB(H), k, h, hb(h), hB(h), K; 21 public final HourStyle base; 22 HourStyle()23 HourStyle() { 24 base = this; 25 } 26 HourStyle(HourStyle base)27 HourStyle(HourStyle base) { 28 this.base = base; 29 } 30 isHourCharacter(String c)31 public static boolean isHourCharacter(String c) { 32 try { 33 HourStyle.valueOf(c); 34 return true; 35 } catch (IllegalArgumentException e) { 36 return false; 37 } 38 } 39 } 40 41 // For splitting the style ID string. 42 private static final Splitter SPACE_SPLITTER = Splitter.on(' ').trimResults(); 43 44 // If this used "getter" method references it wouldn't need so much explicit generic typing. 45 private static final Comparator<PreferredAndAllowedHour> COMPARATOR = 46 Comparator.<PreferredAndAllowedHour, HourStyle>comparing(t -> t.preferred) 47 .thenComparing(t -> t.allowed, lexicographical(Comparator.<HourStyle>naturalOrder())); 48 49 public final HourStyle preferred; 50 /** Unique allowed styles, in the order they were specified during construction. */ 51 public final ImmutableList<HourStyle> allowed; 52 53 /** 54 * Creates a PreferredAndAllowedHour instance with "allowed" styles derived from single 55 * character IDs in the given collection. Note that the iteration order of the allowed 56 * styles is retained. 57 * 58 * <p>This constructor is limiting, since some styles are identified by two character 59 * strings, which cannot be referenced via this constructor. 60 */ PreferredAndAllowedHour(char preferred, Set<Character> allowed)61 public PreferredAndAllowedHour(char preferred, Set<Character> allowed) { 62 this(String.valueOf(preferred), allowed.stream().map(String::valueOf)); 63 } 64 65 /** 66 * Creates a PreferredAndAllowedHour instance with "allowed" styles derived from a space 67 * separated list of unique IDs. Note that the iteration order of the allowed styles is 68 * retained, since in some situations it is necessary that the preferred style should 69 * also be the first allowed style. The list of allowed style IDs must not contain 70 * duplicates. 71 */ PreferredAndAllowedHour(String preferred, String allowedString)72 public PreferredAndAllowedHour(String preferred, String allowedString) { 73 this(preferred, SPACE_SPLITTER.splitToList(allowedString).stream()); 74 } 75 PreferredAndAllowedHour(String preferredStyle, Stream<String> allowedStyles)76 private PreferredAndAllowedHour(String preferredStyle, Stream<String> allowedStyles) { 77 this.preferred = checkNotNull(HourStyle.valueOf(preferredStyle)); 78 this.allowed = allowedStyles.map(HourStyle::valueOf).collect(toImmutableList()); 79 checkArgument(allowed.stream().distinct().count() == allowed.size(), 80 "Allowed (%s) must not contain duplicates", allowed); 81 // Note: In *some* cases the preferred style is required to be the first style in 82 // the allowed set, but not always (thus we cannot do a better check here). 83 // TODO: Figure out if we can enforce preferred == first(allowed) here. 84 checkArgument(allowed.contains(preferred), 85 "Allowed (%s) must contain preferred (%s)", allowed, preferred); 86 } 87 88 @Override compareTo(PreferredAndAllowedHour other)89 public int compareTo(PreferredAndAllowedHour other) { 90 return COMPARATOR.compare(this, other); 91 } 92 93 @Override toString()94 public String toString() { 95 return toString(ImmutableList.of("?")); 96 } 97 toString(Collection<String> regions)98 public String toString(Collection<String> regions) { 99 Joiner withSpaces = Joiner.on(" "); 100 return "<hours preferred=\"" 101 + preferred 102 + "\" allowed=\"" 103 + withSpaces.join(allowed) 104 + "\" regions=\"" 105 + withSpaces.join(regions) 106 + "\"/>"; 107 } 108 109 @Override equals(Object obj)110 public boolean equals(Object obj) { 111 return obj instanceof PreferredAndAllowedHour && compareTo((PreferredAndAllowedHour) obj) == 0; 112 } 113 }