1/*
2 *******************************************************************************
3 *
4 *   Copyright (C) 2004-2014, International Business Machines
5 *   Corporation and others.  All Rights Reserved.
6 *
7 *******************************************************************************
8 *   file name:  UBiDiProps.java
9 *   encoding:   US-ASCII
10 *   tab size:   8 (not used)
11 *   indentation:4
12 *
13 *   created on: 2005jan16
14 *   created by: Markus W. Scherer
15 *
16 *   Low-level Unicode bidi/shaping properties access.
17 *   Java port of ubidi_props.h/.c.
18 */
19
20package com.ibm.icu.impl;
21
22import java.io.IOException;
23import java.nio.ByteBuffer;
24import java.util.Iterator;
25
26import com.ibm.icu.lang.UCharacter;
27import com.ibm.icu.lang.UProperty;
28import com.ibm.icu.text.UnicodeSet;
29import com.ibm.icu.util.ICUUncheckedIOException;
30
31public final class UBiDiProps {
32    // constructors etc. --------------------------------------------------- ***
33
34    // port of ubidi_openProps()
35    private UBiDiProps() throws IOException{
36        ByteBuffer bytes=ICUBinary.getData(DATA_FILE_NAME);
37        readData(bytes);
38    }
39
40    private void readData(ByteBuffer bytes) throws IOException {
41        // read the header
42        ICUBinary.readHeader(bytes, FMT, new IsAcceptable());
43
44        // read indexes[]
45        int i, count;
46        count=bytes.getInt();
47        if(count<IX_TOP) {
48            throw new IOException("indexes[0] too small in "+DATA_FILE_NAME);
49        }
50        indexes=new int[count];
51
52        indexes[0]=count;
53        for(i=1; i<count; ++i) {
54            indexes[i]=bytes.getInt();
55        }
56
57        // read the trie
58        trie=Trie2_16.createFromSerialized(bytes);
59        int expectedTrieLength=indexes[IX_TRIE_SIZE];
60        int trieLength=trie.getSerializedLength();
61        if(trieLength>expectedTrieLength) {
62            throw new IOException(DATA_FILE_NAME+": not enough bytes for the trie");
63        }
64        // skip padding after trie bytes
65        ICUBinary.skipBytes(bytes, expectedTrieLength-trieLength);
66
67        // read mirrors[]
68        count=indexes[IX_MIRROR_LENGTH];
69        if(count>0) {
70            mirrors=new int[count];
71            for(i=0; i<count; ++i) {
72                mirrors[i]=bytes.getInt();
73            }
74        }
75
76        // read jgArray[]
77        count=indexes[IX_JG_LIMIT]-indexes[IX_JG_START];
78        jgArray=new byte[count];
79        for(i=0; i<count; ++i) {
80            jgArray[i]=bytes.get();
81        }
82
83        // read jgArray2[]
84        count=indexes[IX_JG_LIMIT2]-indexes[IX_JG_START2];
85        jgArray2=new byte[count];
86        for(i=0; i<count; ++i) {
87            jgArray2[i]=bytes.get();
88        }
89    }
90
91    // implement ICUBinary.Authenticate
92    private final static class IsAcceptable implements ICUBinary.Authenticate {
93        public boolean isDataVersionAcceptable(byte version[]) {
94            return version[0]==2;
95        }
96    }
97
98    // set of property starts for UnicodeSet ------------------------------- ***
99
100    public final void addPropertyStarts(UnicodeSet set) {
101        int i, length;
102        int c, start, limit;
103
104        byte prev, jg;
105
106        /* add the start code point of each same-value range of the trie */
107        Iterator<Trie2.Range> trieIterator=trie.iterator();
108        Trie2.Range range;
109        while(trieIterator.hasNext() && !(range=trieIterator.next()).leadSurrogate) {
110            set.add(range.startCodePoint);
111        }
112
113        /* add the code points from the bidi mirroring table */
114        length=indexes[IX_MIRROR_LENGTH];
115        for(i=0; i<length; ++i) {
116            c=getMirrorCodePoint(mirrors[i]);
117            set.add(c, c+1);
118        }
119
120        /* add the code points from the Joining_Group array where the value changes */
121        start=indexes[IX_JG_START];
122        limit=indexes[IX_JG_LIMIT];
123        byte[] jga=jgArray;
124        for(;;) {
125            length=limit-start;
126            prev=0;
127            for(i=0; i<length; ++i) {
128                jg=jga[i];
129                if(jg!=prev) {
130                    set.add(start);
131                    prev=jg;
132                }
133                ++start;
134            }
135            if(prev!=0) {
136                /* add the limit code point if the last value was not 0 (it is now start==limit) */
137                set.add(limit);
138            }
139            if(limit==indexes[IX_JG_LIMIT]) {
140                /* switch to the second Joining_Group range */
141                start=indexes[IX_JG_START2];
142                limit=indexes[IX_JG_LIMIT2];
143                jga=jgArray2;
144            } else {
145                break;
146            }
147        }
148
149        /* add code points with hardcoded properties, plus the ones following them */
150
151        /* (none right now) */
152    }
153
154    // property access functions ------------------------------------------- ***
155
156    public final int getMaxValue(int which) {
157        int max;
158
159        max=indexes[IX_MAX_VALUES];
160        switch(which) {
161        case UProperty.BIDI_CLASS:
162            return (max&CLASS_MASK);
163        case UProperty.JOINING_GROUP:
164            return (max&MAX_JG_MASK)>>MAX_JG_SHIFT;
165        case UProperty.JOINING_TYPE:
166            return (max&JT_MASK)>>JT_SHIFT;
167        case UProperty.BIDI_PAIRED_BRACKET_TYPE:
168            return (max&BPT_MASK)>>BPT_SHIFT;
169        default:
170            return -1; /* undefined */
171        }
172    }
173
174    public final int getClass(int c) {
175        return getClassFromProps(trie.get(c));
176    }
177
178    public final boolean isMirrored(int c) {
179        return getFlagFromProps(trie.get(c), IS_MIRRORED_SHIFT);
180    }
181
182    private final int getMirror(int c, int props) {
183        int delta=getMirrorDeltaFromProps(props);
184        if(delta!=ESC_MIRROR_DELTA) {
185            return c+delta;
186        } else {
187            /* look for mirror code point in the mirrors[] table */
188            int m;
189            int i, length;
190            int c2;
191
192            length=indexes[IX_MIRROR_LENGTH];
193
194            /* linear search */
195            for(i=0; i<length; ++i) {
196                m=mirrors[i];
197                c2=getMirrorCodePoint(m);
198                if(c==c2) {
199                    /* found c, return its mirror code point using the index in m */
200                    return getMirrorCodePoint(mirrors[getMirrorIndex(m)]);
201                } else if(c<c2) {
202                    break;
203                }
204            }
205
206            /* c not found, return it itself */
207            return c;
208        }
209    }
210
211    public final int getMirror(int c) {
212        int props=trie.get(c);
213        return getMirror(c, props);
214    }
215
216    public final boolean isBidiControl(int c) {
217        return getFlagFromProps(trie.get(c), BIDI_CONTROL_SHIFT);
218    }
219
220    public final boolean isJoinControl(int c) {
221        return getFlagFromProps(trie.get(c), JOIN_CONTROL_SHIFT);
222    }
223
224    public final int getJoiningType(int c) {
225        return (trie.get(c)&JT_MASK)>>JT_SHIFT;
226    }
227
228    public final int getJoiningGroup(int c) {
229        int start, limit;
230
231        start=indexes[IX_JG_START];
232        limit=indexes[IX_JG_LIMIT];
233        if(start<=c && c<limit) {
234            return (int)jgArray[c-start]&0xff;
235        }
236        start=indexes[IX_JG_START2];
237        limit=indexes[IX_JG_LIMIT2];
238        if(start<=c && c<limit) {
239            return (int)jgArray2[c-start]&0xff;
240        }
241        return UCharacter.JoiningGroup.NO_JOINING_GROUP;
242    }
243
244    public final int getPairedBracketType(int c) {
245        return (trie.get(c)&BPT_MASK)>>BPT_SHIFT;
246    }
247
248    public final int getPairedBracket(int c) {
249        int props=trie.get(c);
250        if((props&BPT_MASK)==0) {
251            return c;
252        } else {
253            return getMirror(c, props);
254        }
255    }
256
257    // data members -------------------------------------------------------- ***
258    private int indexes[];
259    private int mirrors[];
260    private byte jgArray[];
261    private byte jgArray2[];
262
263    private Trie2_16 trie;
264
265    // data format constants ----------------------------------------------- ***
266    private static final String DATA_NAME="ubidi";
267    private static final String DATA_TYPE="icu";
268    private static final String DATA_FILE_NAME=DATA_NAME+"."+DATA_TYPE;
269
270    /* format "BiDi" */
271    private static final int FMT=0x42694469;
272
273    /* indexes into indexes[] */
274    //private static final int IX_INDEX_TOP=0;
275    //private static final int IX_LENGTH=1;
276    private static final int IX_TRIE_SIZE=2;
277    private static final int IX_MIRROR_LENGTH=3;
278
279    private static final int IX_JG_START=4;
280    private static final int IX_JG_LIMIT=5;
281    private static final int IX_JG_START2=6;  /* new in format version 2.2, ICU 54 */
282    private static final int IX_JG_LIMIT2=7;
283
284    private static final int IX_MAX_VALUES=15;
285    private static final int IX_TOP=16;
286
287    // definitions for 16-bit bidi/shaping properties word ----------------- ***
288
289                          /* CLASS_SHIFT=0, */     /* bidi class: 5 bits (4..0) */
290    private static final int JT_SHIFT=5;           /* joining type: 3 bits (7..5) */
291
292    private static final int BPT_SHIFT=8;          /* Bidi_Paired_Bracket_Type(bpt): 2 bits (9..8) */
293
294    private static final int JOIN_CONTROL_SHIFT=10;
295    private static final int BIDI_CONTROL_SHIFT=11;
296
297    private static final int IS_MIRRORED_SHIFT=12;         /* 'is mirrored' */
298    private static final int MIRROR_DELTA_SHIFT=13;        /* bidi mirroring delta: 3 bits (15..13) */
299
300    private static final int MAX_JG_SHIFT=16;              /* max JG value in indexes[MAX_VALUES_INDEX] bits 23..16 */
301
302    private static final int CLASS_MASK=    0x0000001f;
303    private static final int JT_MASK=       0x000000e0;
304    private static final int BPT_MASK=      0x00000300;
305
306    private static final int MAX_JG_MASK=   0x00ff0000;
307
308    private static final int getClassFromProps(int props) {
309        return props&CLASS_MASK;
310    }
311    private static final boolean getFlagFromProps(int props, int shift) {
312        return ((props>>shift)&1)!=0;
313    }
314    private static final int getMirrorDeltaFromProps(int props) {
315        return (short)props>>MIRROR_DELTA_SHIFT;
316    }
317
318    private static final int ESC_MIRROR_DELTA=-4;
319    //private static final int MIN_MIRROR_DELTA=-3;
320    //private static final int MAX_MIRROR_DELTA=3;
321
322    // definitions for 32-bit mirror table entry --------------------------- ***
323
324    /* the source Unicode code point takes 21 bits (20..0) */
325    private static final int MIRROR_INDEX_SHIFT=21;
326    //private static final int MAX_MIRROR_INDEX=0x7ff;
327
328    private static final int getMirrorCodePoint(int m) {
329        return m&0x1fffff;
330    }
331    private static final int getMirrorIndex(int m) {
332        return m>>>MIRROR_INDEX_SHIFT;
333    }
334
335
336    /*
337     * public singleton instance
338     */
339    public static final UBiDiProps INSTANCE;
340
341    // This static initializer block must be placed after
342    // other static member initialization
343    static {
344        try {
345            INSTANCE = new UBiDiProps();
346        } catch (IOException e) {
347            throw new ICUUncheckedIOException(e);
348        }
349    }
350}
351