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