| /* |
| ******************************************************************************* |
| * Copyright (C) 2015-2016, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.impl; |
| |
| import java.util.Collections; |
| import java.util.EnumMap; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import com.ibm.icu.impl.locale.AsciiUtil; |
| import com.ibm.icu.util.UResourceBundle; |
| import com.ibm.icu.util.UResourceBundleIterator; |
| |
| /** |
| * @author markdavis |
| * |
| */ |
| public class ValidIdentifiers { |
| |
| public enum Datatype { |
| currency, |
| language, |
| region, |
| script, |
| subdivision, |
| unit, |
| variant, |
| u, |
| t, |
| x, |
| illegal |
| } |
| |
| public enum Datasubtype { |
| deprecated, |
| private_use, |
| regular, |
| special, |
| unknown, |
| macroregion, |
| } |
| |
| public static class ValiditySet { |
| public final Set<String> regularData; |
| public final Map<String,Set<String>> subdivisionData; |
| public ValiditySet(Set<String> plainData, boolean makeMap) { |
| if (makeMap) { |
| HashMap<String,Set<String>> _subdivisionData = new HashMap<String,Set<String>>(); |
| for (String s : plainData) { |
| int pos = s.indexOf('-'); // read v28 data also |
| int pos2 = pos+1; |
| if (pos < 0) { |
| pos2 = pos = s.charAt(0) < 'A' ? 3 : 2; |
| } |
| final String key = s.substring(0, pos); |
| final String subdivision = s.substring(pos2); |
| |
| Set<String> oldSet = _subdivisionData.get(key); |
| if (oldSet == null) { |
| _subdivisionData.put(key, oldSet = new HashSet<String>()); |
| } |
| oldSet.add(subdivision); |
| } |
| this.regularData = null; |
| HashMap<String,Set<String>> _subdivisionData2 = new HashMap<String,Set<String>>(); |
| // protect the sets |
| for (Entry<String, Set<String>> e : _subdivisionData.entrySet()) { |
| Set<String> value = e.getValue(); |
| // optimize a bit by using singleton |
| Set<String> set = value.size() == 1 ? Collections.singleton(value.iterator().next()) |
| : Collections.unmodifiableSet(value); |
| _subdivisionData2.put(e.getKey(), set); |
| } |
| |
| this.subdivisionData = Collections.unmodifiableMap(_subdivisionData2); |
| } else { |
| this.regularData = Collections.unmodifiableSet(plainData); |
| this.subdivisionData = null; |
| } |
| } |
| |
| public boolean contains(String code) { |
| if (regularData != null) { |
| return regularData.contains(code); |
| } else { |
| int pos = code.indexOf('-'); |
| String key = code.substring(0,pos); |
| final String value = code.substring(pos+1); |
| return contains(key, value); |
| } |
| } |
| |
| public boolean contains(String key, String value) { |
| Set<String> oldSet = subdivisionData.get(key); |
| return oldSet != null && oldSet.contains(value); |
| } |
| |
| @Override |
| public String toString() { |
| if (regularData != null) { |
| return regularData.toString(); |
| } else { |
| return subdivisionData.toString(); |
| } |
| } |
| } |
| |
| private static class ValidityData { |
| static final Map<Datatype,Map<Datasubtype,ValiditySet>> data; |
| static { |
| Map<Datatype, Map<Datasubtype, ValiditySet>> _data = new EnumMap<Datatype,Map<Datasubtype,ValiditySet>>(Datatype.class); |
| UResourceBundle suppData = UResourceBundle.getBundleInstance( |
| ICUResourceBundle.ICU_BASE_NAME, |
| "supplementalData", |
| ICUResourceBundle.ICU_DATA_CLASS_LOADER); |
| UResourceBundle validityInfo = suppData.get("idValidity"); |
| for(UResourceBundleIterator datatypeIterator = validityInfo.getIterator(); |
| datatypeIterator.hasNext();) { |
| UResourceBundle datatype = datatypeIterator.next(); |
| String rawKey = datatype.getKey(); |
| Datatype key = Datatype.valueOf(rawKey); |
| Map<Datasubtype,ValiditySet> values = new EnumMap<Datasubtype,ValiditySet>(Datasubtype.class); |
| for(UResourceBundleIterator datasubtypeIterator = datatype.getIterator(); |
| datasubtypeIterator.hasNext();) { |
| UResourceBundle datasubtype = datasubtypeIterator.next(); |
| String rawsubkey = datasubtype.getKey(); |
| Datasubtype subkey = Datasubtype.valueOf(rawsubkey); |
| // handle single value specially |
| Set<String> subvalues = new HashSet<String>(); |
| if (datasubtype.getType() == UResourceBundle.STRING) { |
| addRange(datasubtype.getString(), subvalues); |
| } else { |
| for (String string : datasubtype.getStringArray()) { |
| addRange(string, subvalues); |
| } |
| } |
| values.put(subkey, new ValiditySet(subvalues, key == Datatype.subdivision)); |
| } |
| _data.put(key, Collections.unmodifiableMap(values)); |
| } |
| data = Collections.unmodifiableMap(_data); |
| } |
| private static void addRange(String string, Set<String> subvalues) { |
| string = AsciiUtil.toLowerString(string); |
| int pos = string.indexOf('~'); |
| if (pos < 0) { |
| subvalues.add(string); |
| } else { |
| StringRange.expand(string.substring(0,pos), string.substring(pos+1), false, subvalues); |
| } |
| } |
| } |
| |
| public static Map<Datatype, Map<Datasubtype, ValiditySet>> getData() { |
| return ValidityData.data; |
| } |
| |
| /** |
| * Returns the Datasubtype containing the code, or null if there is none. |
| */ |
| public static Datasubtype isValid(Datatype datatype, Set<Datasubtype> datasubtypes, String code) { |
| Map<Datasubtype, ValiditySet> subtable = ValidityData.data.get(datatype); |
| if (subtable != null) { |
| for (Datasubtype datasubtype : datasubtypes) { |
| ValiditySet validitySet = subtable.get(datasubtype); |
| if (validitySet != null) { |
| if (validitySet.contains(AsciiUtil.toLowerString(code))) { |
| return datasubtype; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static Datasubtype isValid(Datatype datatype, Set<Datasubtype> datasubtypes, String code, String value) { |
| Map<Datasubtype, ValiditySet> subtable = ValidityData.data.get(datatype); |
| if (subtable != null) { |
| code = AsciiUtil.toLowerString(code); |
| value = AsciiUtil.toLowerString(value); |
| for (Datasubtype datasubtype : datasubtypes) { |
| ValiditySet validitySet = subtable.get(datasubtype); |
| if (validitySet != null) { |
| if (validitySet.contains(code, value)) { |
| return datasubtype; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| } |