386 lines · 12.8 KB · Java
RawDownload
/* * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * (C) Copyright IBM Corp. 1999-2000 - All Rights Reserved * * The original version of this source code and documentation is * copyrighted and owned by IBM. These materials are provided * under terms of a License Agreement between IBM and Sun. * This technology is protected by multiple US and International * patents. This notice and attribution to IBM may not be removed. */ package sun.font; import java.text.Bidi; public final class BidiUtils {       /**     * Return the level of each character into the levels array starting at start.     * This is a convenience method for clients who prefer to use an explicit levels     * array instead of iterating over the runs.     *     * @param levels the array to receive the character levels     * @param start the starting offset into the the array     * @throws IndexOutOfBoundsException if <code>start</code> is less than 0 or     * <code>start + getLength()</code> is greater than <code>levels.length</code>.     */    public static void getLevels(Bidi bidi, byte[] levels, int start) {        int limit = start + bidi.getLength();         if (start < 0 || limit > levels.length) {            throw new IndexOutOfBoundsException("levels.length = " + levels.length +                " start: " + start + " limit: " + limit);        }         int runCount = bidi.getRunCount();        int p = start;        for (int i = 0; i < runCount; ++i) {            int rlimit = start + bidi.getRunLimit(i);            byte rlevel = (byte)bidi.getRunLevel(i);             while (p < rlimit) {                levels[p++] = rlevel;            }        }    }     /**     * Return an array containing the resolved bidi level of each character, in logical order.     * @return an array containing the level of each character, in logical order.     */    public static byte[] getLevels(Bidi bidi) {        byte[] levels = new byte[bidi.getLength()];        getLevels(bidi, levels, 0);        return levels;    }     static final char NUMLEVELS = 62;     /**     * Given level data, compute a a visual to logical mapping.     * The leftmost (or topmost) character is at visual index zero.  The     * logical index of the character is derived from the visual index     * by the expression <code>li = map[vi];</code>.     * @param levels the levels array     * @return the mapping array from visual to logical     */    public static int[] createVisualToLogicalMap(byte[] levels) {        int len = levels.length;        int[] mapping = new int[len];         byte lowestOddLevel = (byte)(NUMLEVELS + 1);        byte highestLevel = 0;         // initialize mapping and levels         for (int i = 0; i < len; i++) {            mapping[i] = i;             byte level = levels[i];            if (level > highestLevel) {                highestLevel = level;            }             if ((level & 0x01) != 0 && level < lowestOddLevel) {                lowestOddLevel = level;            }        }         while (highestLevel >= lowestOddLevel) {            int i = 0;            for (;;) {                while (i < len && levels[i] < highestLevel) {                    i++;                }                int begin = i++;                 if (begin == levels.length) {                    break; // no more runs at this level                }                 while (i < len && levels[i] >= highestLevel) {                    i++;                }                int end = i - 1;                 while (begin < end) {                    int temp = mapping[begin];                    mapping[begin] = mapping[end];                    mapping[end] = temp;                    ++begin;                    --end;                }            }             --highestLevel;        }         return mapping;    }     /**     * Return the inverse position map.  The source array must map one-to-one (each value     * is distinct and the values run from zero to the length of the array minus one).     * For example, if <code>values[i] = j</code>, then <code>inverse[j] = i</code>.     * @param values the source ordering array     * @return the inverse array     */    public static int[] createInverseMap(int[] values) {        if (values == null) {            return null;        }         int[] result = new int[values.length];        for (int i = 0; i < values.length; i++) {            result[values[i]] = i;        }         return result;    }      /**     * Return an array containing contiguous values from 0 to length     * having the same ordering as the source array. If this would be     * a canonical ltr ordering, return null.  The data in values[] is NOT     * required to be a permutation, but elements in values are required     * to be distinct.     * @param values an array containing the discontiguous values     * @return the contiguous values     */    public static int[] createContiguousOrder(int[] values) {        if (values != null) {            return computeContiguousOrder(values, 0, values.length);        }         return null;    }     /**     * Compute a contiguous order for the range start, limit.     */    private static int[] computeContiguousOrder(int[] values, int start,                                                int limit) {         int[] result = new int[limit-start];        for (int i=0; i < result.length; i++) {            result[i] = i + start;        }         // now we'll sort result[], with the following comparison:        // result[i] lessthan result[j] iff values[result[i]] < values[result[j]]         // selection sort for now;  use more elaborate sorts if desired        for (int i=0; i < result.length-1; i++) {            int minIndex = i;            int currentValue = values[result[minIndex]];            for (int j=i; j < result.length; j++) {                if (values[result[j]] < currentValue) {                    minIndex = j;                    currentValue = values[result[minIndex]];                }            }            int temp = result[i];            result[i] = result[minIndex];            result[minIndex] = temp;        }         // shift result by start:        if (start != 0) {            for (int i=0; i < result.length; i++) {                result[i] -= start;            }        }         // next, check for canonical order:        int k;        for (k=0; k < result.length; k++) {            if (result[k] != k) {                break;            }        }         if (k == result.length) {            return null;        }         // now return inverse of result:        return createInverseMap(result);    }     /**     * Return an array containing the data in the values array from start up to limit,     * normalized to fall within the range from 0 up to limit - start.     * If this would be a canonical ltr ordering, return null.     * NOTE: This method assumes that values[] is a logical to visual map     * generated from levels[].     * @param values the source mapping     * @param levels the levels corresponding to the values     * @param start the starting offset in the values and levels arrays     * @param limit the limiting offset in the values and levels arrays     * @return the normlized map     */    public static int[] createNormalizedMap(int[] values, byte[] levels,                                           int start, int limit) {         if (values != null) {            if (start != 0 || limit != values.length) {                // levels optimization                boolean copyRange, canonical;                byte primaryLevel;                 if (levels == null) {                    primaryLevel = (byte) 0x0;                    copyRange = true;                    canonical = true;                }                else {                    if (levels[start] == levels[limit-1]) {                        primaryLevel = levels[start];                        canonical = (primaryLevel & (byte)0x1) == 0;                         // scan for levels below primary                        int i;                        for (i=start; i < limit; i++) {                            if (levels[i] < primaryLevel) {                                break;                            }                            if (canonical) {                                canonical = levels[i] == primaryLevel;                            }                        }                         copyRange = (i == limit);                    }                    else {                        copyRange = false;                         // these don't matter;  but the compiler cares:                        primaryLevel = (byte) 0x0;                        canonical = false;                    }                }                 if (copyRange) {                    if (canonical) {                        return null;                    }                     int[] result = new int[limit-start];                    int baseValue;                     if ((primaryLevel & (byte)0x1) != 0) {                        baseValue = values[limit-1];                    } else {                        baseValue = values[start];                    }                     if (baseValue == 0) {                        System.arraycopy(values, start, result, 0, limit-start);                    }                    else {                        for (int j=0; j < result.length; j++) {                            result[j] = values[j+start] - baseValue;                        }                    }                     return result;                }                else {                    return computeContiguousOrder(values, start, limit);                }            }            else {                return values;            }        }         return null;    }     /**     * Reorder the objects in the array into visual order based on their levels.     * This is a utility function to use when you have a collection of objects     * representing runs of text in logical order, each run containing text     * at a single level.  The elements in the objects array will be reordered     * into visual order assuming each run of text has the level provided     * by the corresponding element in the levels array.     * @param levels an array representing the bidi level of each object     * @param objects the array of objects to be reordered into visual order     */    public static void reorderVisually(byte[] levels, Object[] objects) {        int len = levels.length;         byte lowestOddLevel = (byte)(NUMLEVELS + 1);        byte highestLevel = 0;         // initialize mapping and levels         for (int i = 0; i < len; i++) {            byte level = levels[i];            if (level > highestLevel) {                highestLevel = level;            }             if ((level & 0x01) != 0 && level < lowestOddLevel) {                lowestOddLevel = level;            }        }         while (highestLevel >= lowestOddLevel) {            int i = 0;            for (;;) {                while (i < len && levels[i] < highestLevel) {                    i++;                }                int begin = i++;                 if (begin == levels.length) {                    break; // no more runs at this level                }                 while (i < len && levels[i] >= highestLevel) {                    i++;                }                int end = i - 1;                 while (begin < end) {                    Object temp = objects[begin];                    objects[begin] = objects[end];                    objects[end] = temp;                    ++begin;                    --end;                }            }             --highestLevel;        }    }}