]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/UPropertyAliases.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / UPropertyAliases.java
1 /*\r
2  **********************************************************************\r
3  * Copyright (c) 2002-2010, International Business Machines\r
4  * Corporation and others.  All Rights Reserved.\r
5  **********************************************************************\r
6  * Author: Alan Liu\r
7  * Created: November 5 2002\r
8  * Since: ICU 2.4\r
9  **********************************************************************\r
10  */\r
11 \r
12 package com.ibm.icu.impl;\r
13 \r
14 import java.io.BufferedInputStream;\r
15 import java.io.DataInputStream;\r
16 import java.io.IOException;\r
17 import java.io.InputStream;\r
18 import java.util.MissingResourceException;\r
19 \r
20 import com.ibm.icu.lang.UCharacter;\r
21 import com.ibm.icu.lang.UProperty;\r
22 \r
23 /**\r
24  * Wrapper for the pnames.icu binary data file.  This data file is\r
25  * imported from icu4c.  It contains property and property value\r
26  * aliases from the UCD files PropertyAliases.txt and\r
27  * PropertyValueAliases.txt.  The file is built by the icu4c tool\r
28  * genpname.  It must be built on an ASCII big-endian platform to be\r
29  * usable in icu4j.\r
30  *\r
31  * This class performs two functions.\r
32  *\r
33  * (1) It can import the flat binary data into a tree of usable\r
34  * objects.\r
35  *\r
36  * (2) It provides an API to access the tree of objects.\r
37  *\r
38  * Needless to say, this class is tightly coupled to the binary format\r
39  * of icu4c's pnames.icu file.\r
40  *\r
41  * Each time a UPropertyAliases is constructed, the pnames.icu file is\r
42  * read, parsed, and a data tree assembled.  Clients should create one\r
43  * singleton instance and cache it.\r
44  *\r
45  * @author Alan Liu\r
46  * @since ICU 2.4\r
47  */\r
48 public final class UPropertyAliases implements ICUBinary.Authenticate {\r
49 \r
50     //----------------------------------------------------------------\r
51     // Runtime data.  This is an unflattened representation of the\r
52     // data in pnames.icu.\r
53 \r
54     /**\r
55      * Map from property enum value to nameGroupPool[] index\r
56      */\r
57     private NonContiguousEnumToShort enumToName;\r
58 \r
59     /**\r
60      * Map from property alias to property enum value\r
61      */\r
62     private NameToEnum nameToEnum;\r
63 \r
64     /**\r
65      * Map from property enum value to valueMapArray[] index\r
66      */\r
67     private NonContiguousEnumToShort enumToValue;\r
68 \r
69     /**\r
70      * Each entry represents a binary or enumerated property\r
71      */\r
72     private ValueMap valueMapArray[];\r
73 \r
74     /**\r
75      * Pool of concatenated integer runs.  Each run contains one\r
76      * or more entries.  The last entry of the run is negative.\r
77      * A zero entry indicates "n/a" in the Property*Aliases.txt.\r
78      * Each entry is a stringPool[] index.\r
79      */\r
80     private short nameGroupPool[];\r
81 \r
82     /**\r
83      * Pool of strings.\r
84      */\r
85     private String stringPool[];\r
86 \r
87     //----------------------------------------------------------------\r
88     // Constants\r
89 \r
90     /**\r
91      * Debug flag (not really constant)\r
92      */\r
93     private static boolean DEBUG = ICUDebug.enabled("pnames");\r
94 \r
95     /**\r
96      * File format that this class understands.\r
97      * See icu4c/src/common/propname.h.\r
98      */\r
99     private static final byte DATA_FORMAT_ID[] = {'p', 'n', 'a', 'm'};\r
100 \r
101     /**\r
102      * File version that this class understands.\r
103      * See icu4c/src/common/propname.h.\r
104      */\r
105     private static final byte DATA_FORMAT_VERSION = 1;\r
106 \r
107     /**\r
108      * Name of the datafile\r
109      */\r
110     private static final String DATA_FILE_NAME = ICUResourceBundle.ICU_BUNDLE+"/pnames.icu";\r
111 \r
112     /**\r
113      * Buffer size of datafile.  The whole file is < 16k.\r
114      */\r
115     private static final int DATA_BUFFER_SIZE = 8192;\r
116 \r
117     //----------------------------------------------------------------\r
118     // Constructor\r
119 \r
120     /**\r
121      * Constructs a UPropertyAliases object.  The binary file\r
122      * DATA_FILE_NAME is read from the jar/classpath and unflattened\r
123      * into member variables of this object.\r
124      */\r
125     private UPropertyAliases() throws IOException {\r
126 \r
127         // Open the .icu file from the jar/classpath\r
128         InputStream is = ICUData.getRequiredStream(DATA_FILE_NAME);\r
129         BufferedInputStream b = new BufferedInputStream(is, DATA_BUFFER_SIZE);\r
130         // Read and discard Unicode version...\r
131        /* byte unicodeVersion[] = */ICUBinary.readHeader(b, DATA_FORMAT_ID, this);\r
132         DataInputStream d = new DataInputStream(b);\r
133 \r
134         // Record the origin position of the file.  Keep enough around\r
135         // to seek back to the start of the header.\r
136         d.mark(256);\r
137 \r
138         short enumToName_offset = d.readShort();\r
139         short nameToEnum_offset = d.readShort();\r
140         short enumToValue_offset = d.readShort();\r
141         short total_size = d.readShort();\r
142         short valueMap_offset = d.readShort();\r
143         short valueMap_count = d.readShort();\r
144         short nameGroupPool_offset = d.readShort();\r
145         short nameGroupPool_count = d.readShort();\r
146         short stringPool_offset = d.readShort();\r
147         short stringPool_count = d.readShort();\r
148 \r
149         if (DEBUG) {\r
150             System.out.println(\r
151                "enumToName_offset=" + enumToName_offset + "\n" +\r
152                "nameToEnum_offset=" + nameToEnum_offset + "\n" +\r
153                "enumToValue_offset=" + enumToValue_offset + "\n" +\r
154                "total_size=" + total_size + "\n" +\r
155                "valueMap_offset=" + valueMap_offset + "\n" +\r
156                "valueMap_count=" + valueMap_count + "\n" +\r
157                "nameGroupPool_offset=" + nameGroupPool_offset + "\n" +\r
158                "nameGroupPool_count=" + nameGroupPool_count + "\n" +\r
159                "stringPool_offset=" + stringPool_offset + "\n" +\r
160                "stringPool_count=" + stringPool_count);\r
161         }\r
162 \r
163         // Read it all (less than 32k).  Seeking around (using\r
164         // mark/reset/skipBytes) doesn't work directly on the file,\r
165         // but it works fine if we read everything into a byte[] array\r
166         // first.\r
167         byte raw[] = new byte[total_size];\r
168         d.reset();\r
169         d.readFully(raw);\r
170         d.close();\r
171 \r
172         Builder builder = new Builder(raw);\r
173 \r
174         stringPool = builder.readStringPool(stringPool_offset,\r
175                                             stringPool_count);\r
176 \r
177         nameGroupPool = builder.readNameGroupPool(nameGroupPool_offset,\r
178                                                   nameGroupPool_count);\r
179 \r
180         builder.setupValueMap_map(valueMap_offset, valueMap_count);\r
181 \r
182         // Some of the following data structures have to be set up\r
183         // here, _not_ in Builder.  That's because they are instances\r
184         // of non-static inner classes, and they contain implicit\r
185         // references to this.\r
186 \r
187         builder.seek(enumToName_offset);\r
188         enumToName = new NonContiguousEnumToShort(builder);\r
189         builder.nameGroupOffsetToIndex(enumToName.offsetArray);\r
190 \r
191         builder.seek(nameToEnum_offset);\r
192         nameToEnum = new NameToEnum(builder);\r
193 \r
194         builder.seek(enumToValue_offset);\r
195         enumToValue = new NonContiguousEnumToShort(builder);\r
196         builder.valueMapOffsetToIndex(enumToValue.offsetArray);\r
197 \r
198         valueMapArray = new ValueMap[valueMap_count];\r
199         for (int i=0; i<valueMap_count; ++i) {\r
200             // Must seek to the start of each entry.\r
201             builder.seek(builder.valueMap_map[i]);\r
202             valueMapArray[i] = new ValueMap(builder);\r
203         }\r
204 \r
205         builder.close();\r
206     }\r
207 \r
208     //----------------------------------------------------------------\r
209     // Public API\r
210 \r
211     public static final UPropertyAliases INSTANCE;\r
212 \r
213     static {\r
214         try {\r
215             INSTANCE = new UPropertyAliases();\r
216         } catch(IOException e) {\r
217             ///CLOVER:OFF\r
218             throw new MissingResourceException("Could not construct UPropertyAliases. Missing pnames.icu","","");\r
219             ///CLOVER:ON\r
220         }\r
221     }\r
222 \r
223     /**\r
224      * Return a property name given a property enum.  Multiple\r
225      * names may be available for each property; the nameChoice\r
226      * selects among them.\r
227      */\r
228     public String getPropertyName(int property,\r
229                                   int nameChoice) {\r
230         short nameGroupIndex = enumToName.getShort(property);\r
231         return chooseNameInGroup(nameGroupIndex, nameChoice);\r
232     }\r
233 \r
234     /**\r
235      * Return a property enum given one of its property names.\r
236      * If the property name is not known, this method returns\r
237      * UProperty.UNDEFINED.\r
238      */\r
239     public int getPropertyEnum(String propertyAlias) {\r
240         return nameToEnum.getEnum(propertyAlias);\r
241     }\r
242 \r
243     /**\r
244      * Return a value name given a property enum and a value enum.\r
245      * Multiple names may be available for each value; the nameChoice\r
246      * selects among them.\r
247      */\r
248     public String getPropertyValueName(int property,\r
249                                        int value,\r
250                                        int nameChoice) {\r
251         ValueMap vm = getValueMap(property);\r
252         short nameGroupIndex = vm.enumToName.getShort(value);\r
253         return chooseNameInGroup(nameGroupIndex, nameChoice);\r
254     }\r
255 \r
256     /**\r
257      * Return a value enum given one of its value names and the\r
258      * corresponding property alias.\r
259      */\r
260     public int getPropertyValueEnum(int property,\r
261                                     String valueAlias) {\r
262         ValueMap vm = getValueMap(property);\r
263         return vm.nameToEnum.getEnum(valueAlias);\r
264     }\r
265 \r
266     //----------------------------------------------------------------\r
267     // Data structures\r
268 \r
269     /**\r
270      * A map for the legal values of a binary or enumerated properties.\r
271      */\r
272     private class ValueMap {\r
273 \r
274         /**\r
275          * Maps value enum to index into the nameGroupPool[]\r
276          */\r
277         EnumToShort enumToName; // polymorphic\r
278 \r
279         /**\r
280          * Maps value name to value enum.\r
281          */\r
282         NameToEnum nameToEnum;\r
283 \r
284         ValueMap(Builder b) throws IOException {\r
285             short enumToName_offset = b.readShort();\r
286             short ncEnumToName_offset = b.readShort();\r
287             short nameToEnum_offset = b.readShort();\r
288             if (enumToName_offset != 0) {\r
289                 b.seek(enumToName_offset);\r
290                 ContiguousEnumToShort x = new ContiguousEnumToShort(b);\r
291                 b.nameGroupOffsetToIndex(x.offsetArray);\r
292                 enumToName = x;\r
293             } else {\r
294                 b.seek(ncEnumToName_offset);\r
295                 NonContiguousEnumToShort x = new NonContiguousEnumToShort(b);\r
296                 b.nameGroupOffsetToIndex(x.offsetArray);\r
297                 enumToName = x;\r
298             }\r
299             b.seek(nameToEnum_offset);\r
300             nameToEnum = new NameToEnum(b);\r
301         }\r
302     }\r
303 \r
304     /**\r
305      * Abstract map from enum values to integers.\r
306      */\r
307     private interface EnumToShort {\r
308         short getShort(int enumProbe);\r
309     }\r
310 \r
311     /**\r
312      * Generic map from enum values to offsets.  Enum values are\r
313      * contiguous.\r
314      */\r
315     private static class ContiguousEnumToShort implements EnumToShort {\r
316         int enumStart;\r
317         int enumLimit;\r
318         short offsetArray[];\r
319 \r
320         public short getShort(int enumProbe) {\r
321             if (enumProbe < enumStart || enumProbe >= enumLimit) {\r
322                 throw new IllegalIcuArgumentException("Invalid enum. enumStart = " +enumStart +\r
323                                                    " enumLimit = " + enumLimit +\r
324                                                    " enumProbe = " + enumProbe );\r
325             }\r
326             return offsetArray[enumProbe - enumStart];\r
327         }\r
328 \r
329         ContiguousEnumToShort(ICUBinaryStream s) throws IOException  {\r
330             enumStart = s.readInt();\r
331             enumLimit = s.readInt();\r
332             int count = enumLimit - enumStart;\r
333             offsetArray = new short[count];\r
334             for (int i=0; i<count; ++i) {\r
335                 offsetArray[i] = s.readShort();\r
336             }\r
337         }\r
338     }\r
339 \r
340     /**\r
341      * Generic map from enum values to offsets.  Enum values need not\r
342      * be contiguous.\r
343      */\r
344     private static class NonContiguousEnumToShort implements EnumToShort {\r
345         int enumArray[];\r
346         short offsetArray[];\r
347 \r
348         public short getShort(int enumProbe) {\r
349             for (int i=0; i<enumArray.length; ++i) {\r
350                 if (enumArray[i] < enumProbe) continue;\r
351                 if (enumArray[i] > enumProbe) break;\r
352                 return offsetArray[i];\r
353             }\r
354             throw new IllegalIcuArgumentException("Invalid enum");\r
355         }\r
356 \r
357         NonContiguousEnumToShort(ICUBinaryStream s) throws IOException  {\r
358             int i;\r
359             int count = s.readInt();\r
360             enumArray = new int[count];\r
361             offsetArray = new short[count];\r
362             for (i=0; i<count; ++i) {\r
363                 enumArray[i] = s.readInt();\r
364             }\r
365             for (i=0; i<count; ++i) {\r
366                 offsetArray[i] = s.readShort();\r
367             }\r
368         }\r
369     }\r
370 \r
371     /**\r
372      * Map from names to enum values.\r
373      */\r
374     private class NameToEnum {\r
375         int enumArray[];\r
376         short nameArray[];\r
377 \r
378         int getEnum(String nameProbe) {\r
379             for (int i=0; i<nameArray.length; ++i) {\r
380                 int c = UPropertyAliases.compare(nameProbe,\r
381                                                  stringPool[nameArray[i]]);\r
382                 if (c > 0) continue;\r
383                 if (c < 0) break;\r
384                 return enumArray[i];\r
385             }\r
386             return UProperty.UNDEFINED;\r
387         }\r
388 \r
389         NameToEnum(Builder b) throws IOException {\r
390             int i;\r
391             int count = b.readInt();\r
392             enumArray = new int[count];\r
393             nameArray = new short[count];\r
394             for (i=0; i<count; ++i) {\r
395                 enumArray[i] = b.readInt();\r
396             }\r
397             for (i=0; i<count; ++i) {\r
398                 nameArray[i] = b.stringOffsetToIndex(b.readShort());\r
399             }\r
400         }\r
401     }\r
402 \r
403     //----------------------------------------------------------------\r
404     // Runtime implementation\r
405 \r
406     /**\r
407      * Compare two property names, returning <0, 0, or >0.  The\r
408      * comparison is that described as "loose" matching in the\r
409      * Property*Aliases.txt files.\r
410      */\r
411     public static int compare(String stra, String strb) {\r
412         // Note: This implementation is a literal copy of\r
413         // uprv_comparePropertyNames.  It can probably be improved.\r
414         int istra=0, istrb=0, rc;\r
415         int cstra=0, cstrb=0;\r
416         for (;;) {\r
417             /* Ignore delimiters '-', '_', and ASCII White_Space */\r
418             while (istra<stra.length()) {\r
419                 cstra = stra.charAt(istra);\r
420                 switch (cstra) {\r
421                 case '-':  case '_':  case ' ':  case '\t':\r
422                 case '\n': case 0xb/*\v*/: case '\f': case '\r':\r
423                     ++istra;\r
424                     continue;\r
425                 }\r
426                 break;\r
427             }\r
428 \r
429             while (istrb<strb.length()) {\r
430                 cstrb = strb.charAt(istrb);\r
431                 switch (cstrb) {\r
432                 case '-':  case '_':  case ' ':  case '\t':\r
433                 case '\n': case 0xb/*\v*/: case '\f': case '\r':\r
434                     ++istrb;\r
435                     continue;\r
436                 }\r
437                 break;\r
438             }\r
439 \r
440             /* If we reach the ends of both strings then they match */\r
441             boolean endstra = istra==stra.length();\r
442             boolean endstrb = istrb==strb.length();\r
443             if (endstra) {\r
444                 if (endstrb) return 0;\r
445                 cstra = 0;\r
446             } else if (endstrb) {\r
447                 cstrb = 0;\r
448             }\r
449 \r
450             rc = UCharacter.toLowerCase(cstra) - UCharacter.toLowerCase(cstrb);\r
451             if (rc != 0) {\r
452                 return rc;\r
453             }\r
454 \r
455             ++istra;\r
456             ++istrb;\r
457         }\r
458     }\r
459 \r
460     /**\r
461      * Given an index to a run within the nameGroupPool[], and a\r
462      * nameChoice (0,1,...), select the nameChoice-th entry of the run.\r
463      */\r
464     private String chooseNameInGroup(short nameGroupIndex, int nameChoice) {\r
465         if (nameChoice < 0) {\r
466             throw new IllegalIcuArgumentException("Invalid name choice");\r
467         }\r
468         while (nameChoice-- > 0) {\r
469             if (nameGroupPool[nameGroupIndex++] < 0) {\r
470                 throw new IllegalIcuArgumentException("Invalid name choice");\r
471             }\r
472         }\r
473         short a = nameGroupPool[nameGroupIndex];\r
474         return stringPool[(a < 0) ? -a : a];\r
475     }\r
476 \r
477     /**\r
478      * Return the valueMap[] entry for a given property.\r
479      */\r
480     private ValueMap getValueMap(int property) {\r
481         int valueMapIndex = enumToValue.getShort(property);\r
482         return valueMapArray[valueMapIndex];\r
483     }\r
484 \r
485     //----------------------------------------------------------------\r
486     // ICUBinary API\r
487 \r
488     /**\r
489      * Return true if the given data version can be used.\r
490      */\r
491     public boolean isDataVersionAcceptable(byte version[])   {\r
492         return version[0] == DATA_FORMAT_VERSION;\r
493     }\r
494 \r
495     //----------------------------------------------------------------\r
496     // Builder\r
497 \r
498     /**\r
499      * A specialized ICUBinaryStream that can map between offsets and\r
500      * index values into various arrays (stringPool, nameGroupPool,\r
501      * and valueMap).  It also knows how to read various structures.\r
502      */\r
503     static class Builder extends ICUBinaryStream {\r
504 \r
505         // map[i] = offset of object i.  We need maps for all of our\r
506         // arrays.  The arrays are indexed by offset in the raw binary\r
507         // file; we need to translate that to index.\r
508 \r
509         private short stringPool_map[];\r
510 \r
511         private short valueMap_map[];\r
512 \r
513         private short nameGroup_map[];\r
514 \r
515         public Builder(byte raw[]) {\r
516             super(raw);\r
517         }\r
518 \r
519         /**\r
520          * The valueMap_map[] must be setup in advance.  This method\r
521          * does that.\r
522          */\r
523         public void setupValueMap_map(short offset, short count) {\r
524             valueMap_map = new short[count];\r
525             for (int i=0; i<count; ++i) {\r
526                 // Start of each entry.  Each entry is 6 bytes long.\r
527                 valueMap_map[i] = (short) (offset + i * 6);\r
528             }\r
529         }\r
530 \r
531         /**\r
532          * Read stringPool[].  Build up translation table from offsets\r
533          * to string indices (stringPool_map[]).\r
534          */\r
535         public String[] readStringPool(short offset, short count)\r
536             throws IOException {\r
537             seek(offset);\r
538             // Allocate one more stringPool entry than needed.  Use this\r
539             // to store a "no string" entry in the pool, at index 0.  This\r
540             // maps to offset 0, so let stringPool_map[0] = 0.\r
541             String stringPool[] = new String[count + 1];\r
542             stringPool_map = new short[count + 1];\r
543             short pos = offset;\r
544             StringBuilder buf = new StringBuilder();\r
545             stringPool_map[0] = 0;\r
546             for (int i=1; i<=count; ++i) {\r
547                 buf.setLength(0);\r
548                 for (;;) {\r
549                     // This works because the name is invariant-ASCII\r
550                     char c = (char) readUnsignedByte();\r
551                     if (c == 0) break;\r
552                     buf.append(c);\r
553                 }\r
554                 stringPool_map[i] = pos;\r
555                 stringPool[i] = buf.toString();\r
556                 pos += stringPool[i].length() + 1;\r
557             }\r
558             if (DEBUG) {\r
559                 System.out.println("read stringPool x " + count +\r
560                                    ": " + stringPool[1] + ", " +\r
561                                    stringPool[2] + ", " +\r
562                                    stringPool[3] + ",...");\r
563             }\r
564             return stringPool;\r
565         }\r
566 \r
567         /**\r
568          * Read the nameGroupPool[], and build up the offset->index\r
569          * map (nameGroupPool_map[]).\r
570          */\r
571         public short[] readNameGroupPool(short offset, short count)\r
572             throws IOException {\r
573             // Read nameGroupPool[].  This contains offsets from start of\r
574             // header.  We translate these into indices into stringPool[]\r
575             // on the fly.  The offset 0, which indicates "no entry", we\r
576             // translate into index 0, which contains a null String\r
577             // pointer.\r
578             seek(offset);\r
579             short pos = offset;\r
580             short nameGroupPool[] = new short[count];\r
581             nameGroup_map = new short[count];\r
582             for (int i=0; i<count; ++i) {\r
583                 nameGroup_map[i] = pos;\r
584                 nameGroupPool[i] = stringOffsetToIndex(readShort());\r
585                 pos += 2;\r
586             }\r
587             if (DEBUG) {\r
588                 System.out.println("read nameGroupPool x " + count +\r
589                                    ": " + nameGroupPool[0] + ", " +\r
590                                    nameGroupPool[1] + ", " +\r
591                                    nameGroupPool[2] + ",...");\r
592             }\r
593             return nameGroupPool;\r
594         }\r
595 \r
596         /**\r
597          * Convert an offset into the string pool into a stringPool[]\r
598          * index.\r
599          */\r
600         private short stringOffsetToIndex(short offset) {\r
601             int probe = offset;\r
602             if (probe < 0) probe = -probe;\r
603             for (int i=0; i<stringPool_map.length; ++i) {\r
604                 if (stringPool_map[i] == probe) {\r
605                     return (short) ((offset < 0) ? -i : i);\r
606                 }\r
607             }\r
608             throw new IllegalStateException("Can't map string pool offset " + \r
609                                             offset + " to index");\r
610         }\r
611 \r
612         /**\r
613          * Convert an array of offsets into the string pool into an\r
614          * array of stringPool[] indices.  MODIFIES THE ARRAY IN\r
615          * PLACE.\r
616          */\r
617 /*        private void stringOffsetToIndex(short array[]) {\r
618             for (int i=0; i<array.length; ++i) {\r
619                 array[i] = stringOffsetToIndex(array[i]);\r
620             }\r
621         }*/\r
622 \r
623         /**\r
624          * Convert an offset into the value map into a valueMap[]\r
625          * index.\r
626          */\r
627         private short valueMapOffsetToIndex(short offset) {\r
628             for (short i=0; i<valueMap_map.length; ++i) {\r
629                 if (valueMap_map[i] == offset) {\r
630                     return i;\r
631                 }\r
632             }\r
633             throw new IllegalStateException("Can't map value map offset " + \r
634                                             offset + " to index");\r
635         }\r
636 \r
637         /**\r
638          * Convert an array of offsets into the value map array into\r
639          * an array of valueMap[] indices.  MODIFIES THE ARRAY IN\r
640          * PLACE.\r
641          */\r
642         private void valueMapOffsetToIndex(short array[]) {\r
643             for (int i=0; i<array.length; ++i) {\r
644                 array[i] = valueMapOffsetToIndex(array[i]);\r
645             }\r
646         }\r
647 \r
648         /**\r
649          * Convert an offset into the name group pool into a\r
650          * nameGroupPool[] index.\r
651          */\r
652         private short nameGroupOffsetToIndex(short offset) {\r
653             for (short i=0; i<nameGroup_map.length; ++i) {\r
654                 if (nameGroup_map[i] == offset) {\r
655                     return i;\r
656                 }\r
657             }\r
658             throw new RuntimeException("Can't map name group offset " + offset +\r
659                                        " to index");\r
660         }\r
661 \r
662         /**\r
663          * Convert an array of offsets into the name group pool into an\r
664          * array of nameGroupPool[] indices.  MODIFIES THE ARRAY IN\r
665          * PLACE.\r
666          */\r
667         private void nameGroupOffsetToIndex(short array[]) {\r
668             for (int i=0; i<array.length; ++i) {\r
669                 array[i] = nameGroupOffsetToIndex(array[i]);\r
670             }\r
671         }\r
672     }\r
673 }\r