1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ****************************************************************************** 5 * Copyright (C) 2007-2015, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ****************************************************************************** 8 */ 9 10 package com.ibm.icu.impl.duration.impl; 11 12 import java.io.BufferedReader; 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.io.InputStreamReader; 16 import java.io.UnsupportedEncodingException; 17 import java.util.ArrayList; 18 import java.util.Collection; 19 import java.util.Collections; 20 import java.util.HashMap; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.MissingResourceException; 24 25 import com.ibm.icu.impl.ICUData; 26 import com.ibm.icu.util.ICUUncheckedIOException; 27 28 /** 29 * A PeriodFormatterDataService that serves PeriodFormatterData objects based on 30 * data files stored as resources in this directory. These are text files named 31 * after the locale, for example, 'pfd_he_IL.txt' specifies an period formatter 32 * data file for Hebrew as spoken in Israel. Data is in a JSON-like format. 33 */ 34 public class ResourceBasedPeriodFormatterDataService extends 35 PeriodFormatterDataService { 36 private Collection<String> availableLocales; // of String 37 38 private PeriodFormatterData lastData = null; 39 private String lastLocale = null; 40 private Map<String, PeriodFormatterData> cache = new HashMap<String, PeriodFormatterData>(); // String -> PeriodFormatterData 41 // private PeriodFormatterData fallbackFormatterData; 42 43 private static final String PATH = "data/"; 44 45 private static final ResourceBasedPeriodFormatterDataService singleton = new ResourceBasedPeriodFormatterDataService(); 46 47 /** 48 * Returns the singleton instance of this class. 49 */ 50 public static ResourceBasedPeriodFormatterDataService getInstance() { 51 return singleton; 52 } 53 54 /** 55 * Constructs the service. 56 */ 57 private ResourceBasedPeriodFormatterDataService() { 58 List<String> localeNames = new ArrayList<String>(); // of String 59 InputStream is = ICUData.getRequiredStream(getClass(), PATH 60 + "index.txt"); 61 try { 62 BufferedReader br = new BufferedReader(new InputStreamReader(is, 63 "UTF-8")); 64 String string = null; 65 while (null != (string = br.readLine())) { 66 string = string.trim(); 67 if (string.startsWith("#") || string.length() == 0) { 68 continue; 69 } 70 localeNames.add(string); 71 } 72 br.close(); 73 } catch (IOException e) { 74 throw new IllegalStateException("IO Error reading " + PATH 75 + "index.txt: " + e.toString()); 76 } finally { 77 try { 78 is.close(); 79 } catch (IOException ignored) { 80 } 81 } 82 availableLocales = Collections.unmodifiableList(localeNames); 83 } 84 85 @Override 86 public PeriodFormatterData get(String localeName) { 87 // remove tag info including calendar, we don't use the calendar 88 int x = localeName.indexOf('@'); 89 if (x != -1) { 90 localeName = localeName.substring(0, x); 91 } 92 93 synchronized (this) { 94 if (lastLocale != null && lastLocale.equals(localeName)) { 95 return lastData; 96 } 97 98 PeriodFormatterData ld = cache.get(localeName); 99 if (ld == null) { 100 String ln = localeName; 101 while (!availableLocales.contains(ln)) { 102 int ix = ln.lastIndexOf("_"); 103 if (ix > -1) { 104 ln = ln.substring(0, ix); 105 } else if (!"test".equals(ln)) { 106 ln = "test"; 107 } else { 108 ln = null; 109 break; 110 } 111 } 112 if (ln != null) { 113 String name = PATH + "pfd_" + ln + ".xml"; 114 try { 115 InputStreamReader reader = new InputStreamReader( 116 ICUData.getRequiredStream(getClass(), name), "UTF-8"); 117 DataRecord dr = DataRecord.read(ln, new XMLRecordReader(reader)); 118 reader.close(); 119 if (dr != null) { 120 // debug 121 // if (false && ln.equals("ar_EG")) { 122 // OutputStreamWriter osw = new 123 // OutputStreamWriter(System.out, "UTF-8"); 124 // XMLRecordWriter xrw = new 125 // XMLRecordWriter(osw); 126 // dr.write(xrw); 127 // osw.flush(); 128 // } 129 ld = new PeriodFormatterData(localeName, dr); 130 } 131 } catch (UnsupportedEncodingException e) { 132 throw new MissingResourceException( 133 "Unhandled encoding for resource " + name, name, ""); 134 } catch (IOException e) { 135 throw new ICUUncheckedIOException( 136 "Failed to close() resource " + name, e); 137 } 138 } else { 139 throw new MissingResourceException( 140 "Duration data not found for " + localeName, PATH, 141 localeName); 142 } 143 144 // if (ld == null) { 145 // ld = getFallbackFormatterData(); 146 // } 147 cache.put(localeName, ld); 148 } 149 lastData = ld; 150 lastLocale = localeName; 151 152 return ld; 153 } 154 } 155 156 @Override 157 public Collection<String> getAvailableLocales() { 158 return availableLocales; 159 } 160 161 // PeriodFormatterData getFallbackFormatterData() { 162 // synchronized (this) { 163 // if (fallbackFormatterData == null) { 164 // DataRecord dr = new DataRecord(); // hack, no default, will die if used 165 // fallbackFormatterData = new PeriodFormatterData(null, dr); 166 // } 167 // return fallbackFormatterData; 168 // } 169 // } 170 } 171