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) 2004-2016, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  *******************************************************************************
8  */
9 package com.ibm.icu.impl;
10 
11 import java.nio.ByteBuffer;
12 import java.util.HashMap;
13 import java.util.Set;
14 import java.util.TreeSet;
15 
16 import com.ibm.icu.util.UResourceBundle;
17 import com.ibm.icu.util.UResourceTypeMismatchException;
18 
19 class ICUResourceBundleImpl extends ICUResourceBundle {
20     protected int resource;
21 
ICUResourceBundleImpl(ICUResourceBundleImpl container, String key, int resource)22     protected ICUResourceBundleImpl(ICUResourceBundleImpl container, String key, int resource) {
23         super(container, key);
24         this.resource = resource;
25     }
ICUResourceBundleImpl(WholeBundle wholeBundle)26     ICUResourceBundleImpl(WholeBundle wholeBundle) {
27         super(wholeBundle);
28         resource = wholeBundle.reader.getRootResource();
29     }
getResource()30     public int getResource() {
31         return resource;
32     }
createBundleObject(String _key, int _resource, HashMap<String, String> aliasesVisited, UResourceBundle requested)33     protected final ICUResourceBundle createBundleObject(String _key,
34                                                          int _resource,
35                                                          HashMap<String, String> aliasesVisited,
36                                                          UResourceBundle requested) {
37         switch(ICUResourceBundleReader.RES_GET_TYPE(_resource)) {
38         case STRING :
39         case STRING_V2:
40             return new ICUResourceBundleImpl.ResourceString(this, _key, _resource);
41         case BINARY:
42             return new ICUResourceBundleImpl.ResourceBinary(this, _key, _resource);
43         case ALIAS:
44             return getAliasedResource(this, null, 0, _key, _resource, aliasesVisited, requested);
45         case INT:
46             return new ICUResourceBundleImpl.ResourceInt(this, _key, _resource);
47         case INT_VECTOR:
48             return new ICUResourceBundleImpl.ResourceIntVector(this, _key, _resource);
49         case ARRAY:
50         case ARRAY16:
51             return new ICUResourceBundleImpl.ResourceArray(this, _key, _resource);
52         case TABLE:
53         case TABLE16:
54         case TABLE32:
55             return new ICUResourceBundleImpl.ResourceTable(this, _key, _resource);
56         default :
57             throw new IllegalStateException("The resource type is unknown");
58         }
59     }
60 
61     // Scalar values ------------------------------------------------------- ***
62 
63     private static final class ResourceBinary extends ICUResourceBundleImpl {
64         @Override
getType()65         public int getType() {
66             return BINARY;
67         }
68         @Override
getBinary()69         public ByteBuffer getBinary() {
70             return wholeBundle.reader.getBinary(resource);
71         }
72         @Override
getBinary(byte []ba)73         public byte [] getBinary(byte []ba) {
74             return wholeBundle.reader.getBinary(resource, ba);
75         }
ResourceBinary(ICUResourceBundleImpl container, String key, int resource)76         ResourceBinary(ICUResourceBundleImpl container, String key, int resource) {
77             super(container, key, resource);
78         }
79     }
80     private static final class ResourceInt extends ICUResourceBundleImpl {
81         @Override
getType()82         public int getType() {
83             return INT;
84         }
85         @Override
getInt()86         public int getInt() {
87             return ICUResourceBundleReader.RES_GET_INT(resource);
88         }
89         @Override
getUInt()90         public int getUInt() {
91             return ICUResourceBundleReader.RES_GET_UINT(resource);
92         }
ResourceInt(ICUResourceBundleImpl container, String key, int resource)93         ResourceInt(ICUResourceBundleImpl container, String key, int resource) {
94             super(container, key, resource);
95         }
96     }
97     private static final class ResourceString extends ICUResourceBundleImpl {
98         private String value;
99         @Override
getType()100         public int getType() {
101             return STRING;
102         }
103         @Override
getString()104         public String getString() {
105             if (value != null) {
106                 return value;
107             }
108             return wholeBundle.reader.getString(resource);
109         }
ResourceString(ICUResourceBundleImpl container, String key, int resource)110         ResourceString(ICUResourceBundleImpl container, String key, int resource) {
111             super(container, key, resource);
112             String s = wholeBundle.reader.getString(resource);
113             // Allow the reader cache's SoftReference to do its job.
114             if (s.length() < ICUResourceBundleReader.LARGE_SIZE / 2 ||
115                     CacheValue.futureInstancesWillBeStrong()) {
116                 value = s;
117             }
118         }
119     }
120     private static final class ResourceIntVector extends ICUResourceBundleImpl {
121         @Override
getType()122         public int getType() {
123             return INT_VECTOR;
124         }
125         @Override
getIntVector()126         public int[] getIntVector() {
127             return wholeBundle.reader.getIntVector(resource);
128         }
ResourceIntVector(ICUResourceBundleImpl container, String key, int resource)129         ResourceIntVector(ICUResourceBundleImpl container, String key, int resource) {
130             super(container, key, resource);
131         }
132     }
133 
134     // Container values ---------------------------------------------------- ***
135 
136     static abstract class ResourceContainer extends ICUResourceBundleImpl {
137         protected ICUResourceBundleReader.Container value;
138 
139         @Override
getSize()140         public int getSize() {
141             return value.getSize();
142         }
143         @Override
getString(int index)144         public String getString(int index) {
145             int res = value.getContainerResource(wholeBundle.reader, index);
146             if (res == RES_BOGUS) {
147                 throw new IndexOutOfBoundsException();
148             }
149             String s = wholeBundle.reader.getString(res);
150             if (s != null) {
151                 return s;
152             }
153             return super.getString(index);
154         }
getContainerResource(int index)155         protected int getContainerResource(int index) {
156             return value.getContainerResource(wholeBundle.reader, index);
157         }
createBundleObject(int index, String resKey, HashMap<String, String> aliasesVisited, UResourceBundle requested)158         protected UResourceBundle createBundleObject(int index, String resKey, HashMap<String, String> aliasesVisited,
159                                                      UResourceBundle requested) {
160             int item = getContainerResource(index);
161             if (item == RES_BOGUS) {
162                 throw new IndexOutOfBoundsException();
163             }
164             return createBundleObject(resKey, item, aliasesVisited, requested);
165         }
166 
ResourceContainer(ICUResourceBundleImpl container, String key, int resource)167         ResourceContainer(ICUResourceBundleImpl container, String key, int resource) {
168             super(container, key, resource);
169         }
ResourceContainer(WholeBundle wholeBundle)170         ResourceContainer(WholeBundle wholeBundle) {
171             super(wholeBundle);
172         }
173     }
174     static class ResourceArray extends ResourceContainer {
175         @Override
getType()176         public int getType() {
177             return ARRAY;
178         }
179         @Override
handleGetStringArray()180         protected String[] handleGetStringArray() {
181             ICUResourceBundleReader reader = wholeBundle.reader;
182             int length = value.getSize();
183             String[] strings = new String[length];
184             for (int i = 0; i < length; ++i) {
185                 String s = reader.getString(value.getContainerResource(reader, i));
186                 if (s == null) {
187                     throw new UResourceTypeMismatchException("");
188                 }
189                 strings[i] = s;
190             }
191             return strings;
192         }
193         @Override
getStringArray()194         public String[] getStringArray() {
195             return handleGetStringArray();
196         }
197         @Override
handleGet(String indexStr, HashMap<String, String> aliasesVisited, UResourceBundle requested)198         protected UResourceBundle handleGet(String indexStr, HashMap<String, String> aliasesVisited,
199                                             UResourceBundle requested) {
200             int i = Integer.parseInt(indexStr);
201             return createBundleObject(i, indexStr, aliasesVisited, requested);
202         }
203         @Override
handleGet(int index, HashMap<String, String> aliasesVisited, UResourceBundle requested)204         protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited,
205                                             UResourceBundle requested) {
206             return createBundleObject(index, Integer.toString(index), aliasesVisited, requested);
207         }
ResourceArray(ICUResourceBundleImpl container, String key, int resource)208         ResourceArray(ICUResourceBundleImpl container, String key, int resource) {
209             super(container, key, resource);
210             value = wholeBundle.reader.getArray(resource);
211         }
212     }
213     static class ResourceTable extends ResourceContainer {
214         @Override
getType()215         public int getType() {
216             return TABLE;
217         }
getKey(int index)218         protected String getKey(int index) {
219             return ((ICUResourceBundleReader.Table)value).getKey(wholeBundle.reader, index);
220         }
221         @Override
handleKeySet()222         protected Set<String> handleKeySet() {
223             ICUResourceBundleReader reader = wholeBundle.reader;
224             TreeSet<String> keySet = new TreeSet<String>();
225             ICUResourceBundleReader.Table table = (ICUResourceBundleReader.Table)value;
226             for (int i = 0; i < table.getSize(); ++i) {
227                 keySet.add(table.getKey(reader, i));
228             }
229             return keySet;
230         }
231         @Override
handleGet(String resKey, HashMap<String, String> aliasesVisited, UResourceBundle requested)232         protected UResourceBundle handleGet(String resKey, HashMap<String, String> aliasesVisited,
233                                             UResourceBundle requested) {
234             int i = ((ICUResourceBundleReader.Table)value).findTableItem(wholeBundle.reader, resKey);
235             if (i < 0) {
236                 return null;
237             }
238             return createBundleObject(resKey, getContainerResource(i), aliasesVisited, requested);
239         }
240         @Override
handleGet(int index, HashMap<String, String> aliasesVisited, UResourceBundle requested)241         protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited,
242                                             UResourceBundle requested) {
243             String itemKey = ((ICUResourceBundleReader.Table)value).getKey(wholeBundle.reader, index);
244             if (itemKey == null) {
245                 throw new IndexOutOfBoundsException();
246             }
247             return createBundleObject(itemKey, getContainerResource(index), aliasesVisited, requested);
248         }
249         @Override
handleGetObject(String key)250         protected Object handleGetObject(String key) {
251             // Fast path for common cases: Avoid creating UResourceBundles if possible.
252             // It would be even better if we could override getString(key)/getStringArray(key),
253             // so that we know the expected object type,
254             // but those are final in java.util.ResourceBundle.
255             ICUResourceBundleReader reader = wholeBundle.reader;
256             int index = ((ICUResourceBundleReader.Table)value).findTableItem(reader, key);
257             if (index >= 0) {
258                 int res = value.getContainerResource(reader, index);
259                 // getString(key)
260                 String s = reader.getString(res);
261                 if (s != null) {
262                     return s;
263                 }
264                 // getStringArray(key)
265                 ICUResourceBundleReader.Container array = reader.getArray(res);
266                 if (array != null) {
267                     int length = array.getSize();
268                     String[] strings = new String[length];
269                     for (int j = 0;; ++j) {
270                         if (j == length) {
271                             return strings;
272                         }
273                         s = reader.getString(array.getContainerResource(reader, j));
274                         if (s == null) {
275                             // Equivalent to resolveObject(key, requested):
276                             // If this is not a string array,
277                             // then build and return a UResourceBundle.
278                             break;
279                         }
280                         strings[j] = s;
281                     }
282                 }
283             }
284             return super.handleGetObject(key);
285         }
286         /**
287          * Returns a String if found, or null if not found or if the key item is not a string.
288          */
findString(String key)289         String findString(String key) {
290             ICUResourceBundleReader reader = wholeBundle.reader;
291             int index = ((ICUResourceBundleReader.Table)value).findTableItem(reader, key);
292             if (index < 0) {
293                 return null;
294             }
295             return reader.getString(value.getContainerResource(reader, index));
296         }
ResourceTable(ICUResourceBundleImpl container, String key, int resource)297         ResourceTable(ICUResourceBundleImpl container, String key, int resource) {
298             super(container, key, resource);
299             value = wholeBundle.reader.getTable(resource);
300         }
301         /**
302          * Constructor for the root table of a bundle.
303          */
ResourceTable(WholeBundle wholeBundle, int rootRes)304         ResourceTable(WholeBundle wholeBundle, int rootRes) {
305             super(wholeBundle);
306             value = wholeBundle.reader.getTable(rootRes);
307         }
308     }
309 }
310