📄 bidi.java
字号:
/* Bidi.java -- Bidirectional Algorithm implementation Copyright (C) 2005, 2006 Free Software Foundation, Inc.This file is part of GNU Classpath.GNU Classpath is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version. GNU Classpath is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNUGeneral Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU Classpath; see the file COPYING. If not, write to theFree Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA02110-1301 USA.Linking this library statically or dynamically with other modules ismaking a combined work based on this library. Thus, the terms andconditions of the GNU General Public License cover the wholecombination.As a special exception, the copyright holders of this library give youpermission to link this library with independent modules to produce anexecutable, regardless of the license terms of these independentmodules, and to copy and distribute the resulting executable underterms of your choice, provided that you also meet, for each linkedindependent module, the terms and conditions of the license of thatmodule. An independent module is a module which is not derived fromor based on this library. If you modify this library, you may extendthis exception to your version of the library, but you are notobligated to do so. If you do not wish to do so, delete thisexception statement from your version. */package java.text;import java.awt.font.NumericShaper;import java.awt.font.TextAttribute;import java.util.ArrayList;/** * Bidirectional Algorithm implementation. * * The full algorithm is * <a href="http://www.unicode.org/unicode/reports/tr9/">Unicode Standard * Annex #9: The Bidirectional Algorithm</a>. * * @since 1.4 */public final class Bidi{ /** * This indicates that a strongly directional character in the text should * set the initial direction, but if no such character is found, then the * initial direction will be left-to-right. */ public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2; /** * This indicates that a strongly directional character in the text should * set the initial direction, but if no such character is found, then the * initial direction will be right-to-left. */ public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1; /** * This indicates that the initial direction should be left-to-right. */ public static final int DIRECTION_LEFT_TO_RIGHT = 0; /** * This indicates that the initial direction should be right-to-left. */ public static final int DIRECTION_RIGHT_TO_LEFT = 1; // Flags used when computing the result. private static final int LTOR = 1 << DIRECTION_LEFT_TO_RIGHT; private static final int RTOL = 1 << DIRECTION_RIGHT_TO_LEFT; // The text we are examining, and the starting offset. // If we had a better way to handle createLineBidi, we wouldn't // need this at all -- which for the String case would be an // efficiency win. private char[] text; private int textOffset; // The embeddings corresponding to the text, and the starting offset. private byte[] embeddings; private int embeddingOffset; // The length of the text (and embeddings) to use. private int length; // The flags. private int flags; // All instance fields following this point are initialized // during analysis. Fields before this must be set by the constructor. // The initial embedding level. private int baseEmbedding; // The type of each character in the text. private byte[] types; // The levels we compute. private byte[] levels; // A list of indices where a formatting code was found. These // are indicies into the original text -- not into the text after // the codes have been removed. private ArrayList formatterIndices; // Indices of the starts of runs in the text. private int[] runs; // A convenience field where we keep track of what kinds of runs // we've seen. private int resultFlags; /** * Create a new Bidi object given an attributed character iterator. * This constructor will examine various attributes of the text: * <ul> * <li> {@link TextAttribute#RUN_DIRECTION} is used to determine the * paragraph's base embedding level. This constructor will recognize * either {@link TextAttribute#RUN_DIRECTION_LTR} or * {@link TextAttribute#RUN_DIRECTION_RTL}. If neither is given, * {@link #DIRECTION_DEFAULT_LEFT_TO_RIGHT} is assumed. * </li> * * <li> If {@link TextAttribute#NUMERIC_SHAPING} is seen, then numeric * shaping will be done before the Bidi algorithm is run. * </li> * * <li> If {@link TextAttribute#BIDI_EMBEDDING} is seen on a given * character, then the value of this attribute will be used as an * embedding level override. * </li> * </ul> * @param iter the attributed character iterator to use */ public Bidi(AttributedCharacterIterator iter) { // If set, this attribute should be set on all characters. // We don't check this (should we?) but we do assume that we // can simply examine the first character. Object val = iter.getAttribute(TextAttribute.RUN_DIRECTION); if (val == TextAttribute.RUN_DIRECTION_LTR) this.flags = DIRECTION_LEFT_TO_RIGHT; else if (val == TextAttribute.RUN_DIRECTION_RTL) this.flags = DIRECTION_RIGHT_TO_LEFT; else this.flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT; // Likewise this attribute should be specified on the whole text. // We read it here and then, if it is set, we apply the numeric shaper // to the text before processing it. NumericShaper shaper = null; val = iter.getAttribute(TextAttribute.NUMERIC_SHAPING); if (val instanceof NumericShaper) shaper = (NumericShaper) val; char[] text = new char[iter.getEndIndex() - iter.getBeginIndex()]; this.embeddings = new byte[this.text.length]; this.embeddingOffset = 0; this.length = text.length; for (int i = 0; i < this.text.length; ++i) { this.text[i] = iter.current(); val = iter.getAttribute(TextAttribute.BIDI_EMBEDDING); if (val instanceof Integer) { int ival = ((Integer) val).intValue(); byte bval; if (ival < -62 || ival > 62) bval = 0; else bval = (byte) ival; this.embeddings[i] = bval; } } // Invoke the numeric shaper, if specified. if (shaper != null) shaper.shape(this.text, 0, this.length); runBidi(); } /** * Create a new Bidi object with the indicated text and, possibly, explicit * embedding settings. * * If the embeddings array is null, it is ignored. Otherwise it is taken to * be explicit embedding settings corresponding to the text. Positive values * from 1 to 61 are embedding levels, and negative values from -1 to -61 are * embedding overrides. (FIXME: not at all clear what this really means.) * * @param text the text to use * @param offset the offset of the first character of the text * @param embeddings the explicit embeddings, or null if there are none * @param embedOffset the offset of the first embedding value to use * @param length the length of both the text and the embeddings * @param flags a flag indicating the base embedding direction */ public Bidi(char[] text, int offset, byte[] embeddings, int embedOffset, int length, int flags) { if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT && flags != DIRECTION_LEFT_TO_RIGHT && flags != DIRECTION_RIGHT_TO_LEFT) throw new IllegalArgumentException("unrecognized 'flags' argument: " + flags); this.text = text; this.textOffset = offset; this.embeddings = embeddings; this.embeddingOffset = embedOffset; this.length = length; this.flags = flags; runBidi(); } /** * Create a new Bidi object using the contents of the given String * as the text. * @param text the text to use * @param flags a flag indicating the base embedding direction */ public Bidi(String text, int flags) { if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT && flags != DIRECTION_LEFT_TO_RIGHT && flags != DIRECTION_RIGHT_TO_LEFT) throw new IllegalArgumentException("unrecognized 'flags' argument: " + flags); // This is inefficient, but it isn't clear whether it matters. // If it does we can change our implementation a bit to allow either // a String or a char[]. this.text = text.toCharArray(); this.textOffset = 0; this.embeddings = null; this.embeddingOffset = 0; this.length = text.length(); this.flags = flags; runBidi(); } /** * Implementation function which computes the initial type of * each character in the input. */ private void computeTypes() { types = new byte[length]; for (int i = 0; i < length; ++i) types[i] = Character.getDirectionality(text[textOffset + i]); } /** * An internal function which implements rules P2 and P3. * This computes the base embedding level. * @return the paragraph's base embedding level */ private int computeParagraphEmbeddingLevel() { // First check to see if the user supplied a directionality override. if (flags == DIRECTION_LEFT_TO_RIGHT || flags == DIRECTION_RIGHT_TO_LEFT) return flags; // This implements rules P2 and P3. // (Note that we don't need P1, as the user supplies // a paragraph.) for (int i = 0; i < length; ++i) { int dir = types[i]; if (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT) return DIRECTION_LEFT_TO_RIGHT; if (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT || dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT) return DIRECTION_RIGHT_TO_LEFT; } return (flags == DIRECTION_DEFAULT_LEFT_TO_RIGHT ? DIRECTION_LEFT_TO_RIGHT : DIRECTION_RIGHT_TO_LEFT); } /** * An internal function which implements rules X1 through X9. * This computes the initial levels for the text, handling * explicit overrides and embeddings. */ private void computeExplicitLevels() { levels = new byte[length]; byte currentEmbedding = (byte) baseEmbedding; // The directional override is a Character directionality // constant. -1 means there is no override. byte directionalOverride = -1; // The stack of pushed embeddings, and the stack pointer. // Note that because the direction is inherent in the depth, // and because we have a bit left over in a byte, we can encode // the override, if any, directly in this value on the stack. final int MAX_DEPTH = 62; byte[] embeddingStack = new byte[MAX_DEPTH]; int sp = 0; for (int i = 0; i < length; ++i) { // If we see an explicit embedding, we use that, even if // the current character is itself a directional override. if (embeddings != null && embeddings[embeddingOffset + i] != 0) { // It isn't at all clear what we're supposed to do here. // What does a negative value really mean? // Should we push on the embedding stack here? currentEmbedding = embeddings[embeddingOffset + i]; if (currentEmbedding < 0) { currentEmbedding = (byte) -currentEmbedding; directionalOverride = (((currentEmbedding % 2) == 0) ? Character.DIRECTIONALITY_LEFT_TO_RIGHT : Character.DIRECTIONALITY_RIGHT_TO_LEFT); } else directionalOverride = -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -