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 */
9package com.ibm.icu.impl;
10
11import java.nio.ByteBuffer;
12import java.util.HashMap;
13import java.util.Set;
14import java.util.TreeSet;
15
16import com.ibm.icu.util.UResourceBundle;
17import com.ibm.icu.util.UResourceTypeMismatchException;
18
19class ICUResourceBundleImpl extends ICUResourceBundle {
20    protected int resource;
21
22    protected ICUResourceBundleImpl(ICUResourceBundleImpl container, String key, int resource) {
23        super(container, key);
24        this.resource = resource;
25    }
26    ICUResourceBundleImpl(WholeBundle wholeBundle) {
27        super(wholeBundle);
28        resource = wholeBundle.reader.getRootResource();
29    }
30    public int getResource() {
31        return resource;
32    }
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
65        public int getType() {
66            return BINARY;
67        }
68        @Override
69        public ByteBuffer getBinary() {
70            return wholeBundle.reader.getBinary(resource);
71        }
72        @Override
73        public byte [] getBinary(byte []ba) {
74            return wholeBundle.reader.getBinary(resource, ba);
75        }
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
82        public int getType() {
83            return INT;
84        }
85        @Override
86        public int getInt() {
87            return ICUResourceBundleReader.RES_GET_INT(resource);
88        }
89        @Override
90        public int getUInt() {
91            return ICUResourceBundleReader.RES_GET_UINT(resource);
92        }
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
100        public int getType() {
101            return STRING;
102        }
103        @Override
104        public String getString() {
105            if (value != null) {
106                return value;
107            }
108            return wholeBundle.reader.getString(resource);
109        }
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
122        public int getType() {
123            return INT_VECTOR;
124        }
125        @Override
126        public int[] getIntVector() {
127            return wholeBundle.reader.getIntVector(resource);
128        }
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
140        public int getSize() {
141            return value.getSize();
142        }
143        @Override
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        }
155        protected int getContainerResource(int index) {
156            return value.getContainerResource(wholeBundle.reader, index);
157        }
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
167        ResourceContainer(ICUResourceBundleImpl container, String key, int resource) {
168            super(container, key, resource);
169        }
170        ResourceContainer(WholeBundle wholeBundle) {
171            super(wholeBundle);
172        }
173    }
174    static class ResourceArray extends ResourceContainer {
175        @Override
176        public int getType() {
177            return ARRAY;
178        }
179        @Override
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
194        public String[] getStringArray() {
195            return handleGetStringArray();
196        }
197        @Override
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
204        protected UResourceBundle handleGet(int index, HashMap<String, String> aliasesVisited,
205                                            UResourceBundle requested) {
206            return createBundleObject(index, Integer.toString(index), aliasesVisited, requested);
207        }
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
215        public int getType() {
216            return TABLE;
217        }
218        protected String getKey(int index) {
219            return ((ICUResourceBundleReader.Table)value).getKey(wholeBundle.reader, index);
220        }
221        @Override
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
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
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
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         */
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        }
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         */
304        ResourceTable(WholeBundle wholeBundle, int rootRes) {
305            super(wholeBundle);
306            value = wholeBundle.reader.getTable(rootRes);
307        }
308    }
309}
310