1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.deskclock.data; 18 19 import java.text.Collator; 20 import java.util.Comparator; 21 import java.util.TimeZone; 22 23 /** 24 * A read-only domain object representing a city of the world and associated time information. It 25 * also contains static comparators that can be instantiated to order cities in common sort orders. 26 */ 27 public final class City { 28 29 /** A unique identifier for the city. */ 30 private final String mId; 31 32 /** An optional numeric index used to order cities for display; -1 if no such index exists. */ 33 private final int mIndex; 34 35 /** An index string used to order cities for display. */ 36 private final String mIndexString; 37 38 /** The display name of the city. */ 39 private final String mName; 40 41 /** The phonetic name of the city used to order cities for display. */ 42 private final String mPhoneticName; 43 44 /** The {@link TimeZone#getID() id} of the timezone in which the city is located. */ 45 private final String mTimeZoneId; 46 47 /** The TimeZone corresponding to the {@link #mTimeZoneId}. */ 48 private final TimeZone mTimeZone; 49 50 /** A cached upper case form of the {@link #mName} used in case-insensitive name comparisons. */ 51 private String mNameUpperCase; 52 City(String id, int index, String indexString, String name, String phoneticName, String timeZoneId)53 City(String id, int index, String indexString, String name, String phoneticName, 54 String timeZoneId) { 55 mId = id; 56 mIndex = index; 57 mIndexString = indexString; 58 mName = name; 59 mPhoneticName = phoneticName; 60 mTimeZoneId = timeZoneId; 61 mTimeZone = TimeZone.getTimeZone(mTimeZoneId); 62 } 63 getId()64 public String getId() { return mId; } getIndex()65 public int getIndex() { return mIndex; } getName()66 public String getName() { return mName; } getTimeZone()67 public TimeZone getTimeZone() { return mTimeZone; } getTimeZoneId()68 public String getTimeZoneId() { return mTimeZoneId; } getIndexString()69 public String getIndexString() { return mIndexString; } getPhoneticName()70 public String getPhoneticName() { return mPhoneticName; } 71 getNameUpperCase()72 public String getNameUpperCase() { 73 if (mNameUpperCase == null) { 74 mNameUpperCase = mName.toUpperCase(); 75 } 76 return mNameUpperCase; 77 } 78 79 @Override toString()80 public String toString() { 81 return String.format("City {id=%s, index=%d, indexString=%s, name=%s, phonetic=%s, tz=%s}", 82 mId, mIndex, mIndexString, mName, mPhoneticName, mTimeZoneId); 83 } 84 85 /** 86 * Orders by: 87 * 88 * <ol> 89 * <li>UTC offset of {@link #getTimeZone() timezone}</li> 90 * <li>{@link #getIndex() numeric index}</li> 91 * <li>{@link #getIndexString()} alphabetic index}</li> 92 * <li>{@link #getPhoneticName() phonetic name}</li> 93 * </ol> 94 */ 95 public static final class UtcOffsetComparator implements Comparator<City> { 96 97 private final Comparator<City> mDelegate1 = new UtcOffsetIndexComparator();; 98 99 private final Comparator<City> mDelegate2 = new NameComparator(); 100 compare(City c1, City c2)101 public int compare(City c1, City c2) { 102 int result = mDelegate1.compare(c1, c2); 103 104 if (result == 0) { 105 result = mDelegate2.compare(c1, c2); 106 } 107 108 return result; 109 } 110 } 111 112 /** 113 * Orders by: 114 * 115 * <ol> 116 * <li>UTC offset of {@link #getTimeZone() timezone}</li> 117 * </ol> 118 */ 119 public static final class UtcOffsetIndexComparator implements Comparator<City> { 120 121 // Snapshot the current time when the Comparator is created to obtain consistent offsets. 122 private final long now = System.currentTimeMillis(); 123 compare(City c1, City c2)124 public int compare(City c1, City c2) { 125 final int utcOffset1 = c1.getTimeZone().getOffset(now); 126 final int utcOffset2 = c2.getTimeZone().getOffset(now); 127 return Integer.compare(utcOffset1, utcOffset2); 128 } 129 } 130 131 /** 132 * This comparator sorts using the city fields that influence natural name sort order: 133 * 134 * <ol> 135 * <li>{@link #getIndex() numeric index}</li> 136 * <li>{@link #getIndexString()} alphabetic index}</li> 137 * <li>{@link #getPhoneticName() phonetic name}</li> 138 * </ol> 139 */ 140 public static final class NameComparator implements Comparator<City> { 141 142 private final Comparator<City> mDelegate = new NameIndexComparator(); 143 144 // Locale-sensitive comparator for phonetic names. 145 private final Collator mNameCollator = Collator.getInstance(); 146 147 @Override compare(City c1, City c2)148 public int compare(City c1, City c2) { 149 int result = mDelegate.compare(c1, c2); 150 151 if (result == 0) { 152 result = mNameCollator.compare(c1.getPhoneticName(), c2.getPhoneticName()); 153 } 154 155 return result; 156 } 157 } 158 159 /** 160 * Orders by: 161 * 162 * <ol> 163 * <li>{@link #getIndex() numeric index}</li> 164 * <li>{@link #getIndexString()} alphabetic index}</li> 165 * </ol> 166 */ 167 public static final class NameIndexComparator implements Comparator<City> { 168 169 // Locale-sensitive comparator for index strings. 170 private final Collator mNameCollator = Collator.getInstance(); 171 172 @Override compare(City c1, City c2)173 public int compare(City c1, City c2) { 174 int result = Integer.compare(c1.getIndex(), c2.getIndex()); 175 176 if (result == 0) { 177 result = mNameCollator.compare(c1.getIndexString(), c2.getIndexString()); 178 } 179 180 return result; 181 } 182 } 183 }