1 /*******************************************************************************
2  * Copyright (c) 2011 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  ******************************************************************************/
14 package org.eclipse.equinox.bidi.custom;
15 
16 import org.eclipse.equinox.bidi.advanced.IStructuredTextExpert;
17 import org.eclipse.equinox.bidi.advanced.StructuredTextEnvironment;
18 
19 /**
20  * Provides services related to the bidi classification of characters.
21  */
22 public class StructuredTextCharTypes {
23 
24 	// In the following lines, B, L, R and AL represent bidi categories
25 	// as defined in the Unicode Bidirectional Algorithm
26 	// ( http://www.unicode.org/reports/tr9/ ).
27 	// B  represents the category Block Separator.
28 	// L  represents the category Left to Right character.
29 	// R  represents the category Right to Left character.
30 	// AL represents the category Arabic Letter.
31 	// AN represents the category Arabic Number.
32 	// EN  represents the category European Number.
33 	static final byte B = Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR;
34 	static final byte L = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
35 	static final byte R = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
36 	static final byte AL = Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC;
37 	static final byte AN = Character.DIRECTIONALITY_ARABIC_NUMBER;
38 	static final byte EN = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
39 
40 	private static final int CHARTYPES_ADD = 2;
41 
42 	/**
43 	 * The IStructuredTextExpert instance which created this instance.
44 	 */
45 	final protected IStructuredTextExpert expert;
46 	/**
47 	 * The StructuredTextTypeHandler instance utilized by the expert.
48 	 */
49 	final protected StructuredTextTypeHandler handler;
50 	/**
51 	 * The environment associated with the expert.
52 	 */
53 	final protected StructuredTextEnvironment environment;
54 	/**
55 	 * The source text whose characters are analyzed.
56 	 */
57 	final protected String text;
58 
59 	// 1 byte for each char in text
60 	private byte[] types;
61 
62 	// structured text direction. -1 means not yet computed; -2 means within handler.getDirection
63 	private int direction = -1;
64 
65 	/**
66 	 *  Constructor
67 	 *
68 	 * @param  expert IStructuredTextExpert instance through which this handler
69 	 *         is invoked. The handler can use IStructuredTextExpert methods to
70 	 *         query items stored in the expert instance, like the current
71 	 *         {@link StructuredTextEnvironment environment}.
72 	 *
73 	 *  @param text is the text whose characters are analyzed.
74 	 */
StructuredTextCharTypes(IStructuredTextExpert expert, String text)75 	public StructuredTextCharTypes(IStructuredTextExpert expert, String text) {
76 		this.expert = expert;
77 		this.handler = expert.getTypeHandler();
78 		this.environment = expert.getEnvironment();
79 		this.text = text;
80 		types = new byte[text.length()];
81 	}
82 
83 	/**
84 	 * Indicates the base text direction appropriate for an instance of
85 	 * structured text.
86 	 *
87 	 * @return the base direction of the structured text. This direction
88 	 *         may not be the same depending on the environment and on
89 	 *         whether the structured text contains Arabic or Hebrew
90 	 *         letters.<br>
91 	 *         The value returned is either
92 	 *         {@link IStructuredTextExpert#DIR_LTR DIR_LTR} or
93 	 *         {@link IStructuredTextExpert#DIR_RTL DIR_RTL}.
94 	 */
getDirection()95 	public int getDirection() {
96 		if (direction < 0)
97 			direction = handler.getDirection(expert, text, this);
98 		return direction;
99 	}
100 
getCachedTypeAt(int index)101 	private byte getCachedTypeAt(int index) {
102 		return (byte) (types[index] - CHARTYPES_ADD);
103 	}
104 
hasCachedTypeAt(int i)105 	private boolean hasCachedTypeAt(int i) {
106 		return (types[i] != 0); // "0" means "unknown"
107 	}
108 
109 	/**
110 	 *  Gets the directionality of the character in the original string
111 	 *  at the specified index.
112 	 *
113 	 *  @param  index position of the character in the <i>lean</i> text
114 	 *
115 	 *  @return the bidi type of the character. It is one of the
116 	 *          values which can be returned by
117 	 *          {@link Character#getDirectionality(char)}.
118 	 */
getBidiTypeAt(int index)119 	public byte getBidiTypeAt(int index) {
120 		if (hasCachedTypeAt(index))
121 			return getCachedTypeAt(index);
122 		byte charType = Character.getDirectionality(text.charAt(index));
123 		if (charType == B) {
124 			if (direction < 0) {
125 				if (direction < -1) // called by handler.getDirection
126 					return charType; // avoid infinite recursion
127 				direction = -2; // signal we go within handler.getDirection
128 				direction = handler.getDirection(expert, text, this);
129 			}
130 			charType = (direction == StructuredTextEnvironment.ORIENT_RTL) ? R : L;
131 		}
132 		setBidiTypeAt(index, charType);
133 		return charType;
134 	}
135 
136 	/**
137 	 *  Forces a bidi type on a character.
138 	 *
139 	 *  @param  index position of the character whose bidi type is set.
140 	 *
141 	 *  @param  charType bidirectional type of the character. It must be
142 	 *          one of the values which can be returned by
143 	 *          <code>java.lang.Character.getDirectionality</code>.
144 	 */
setBidiTypeAt(int index, byte charType)145 	public void setBidiTypeAt(int index, byte charType) {
146 		types[index] = (byte) (charType + CHARTYPES_ADD);
147 	}
148 
149 	/**
150 	 *  Gets the orientation of the component in which the text will
151 	 *  be displayed.
152 	 *
153 	 *  @return the orientation as either
154 	 *          {@link StructuredTextEnvironment#ORIENT_LTR},
155 	 *          {@link StructuredTextEnvironment#ORIENT_RTL},
156 	 *          {@link StructuredTextEnvironment#ORIENT_UNKNOWN} or
157 	 *          {@link StructuredTextEnvironment#ORIENT_IGNORE}.
158 	 */
resolveOrientation()159 	public int resolveOrientation() {
160 		int orient = environment.getOrientation();
161 		if ((orient & StructuredTextEnvironment.ORIENT_CONTEXTUAL) == 0) { // absolute orientation
162 			return orient;
163 		}
164 		// contextual orientation:
165 		orient &= ~StructuredTextEnvironment.ORIENT_CONTEXTUAL; // initiate to the default orientation minus contextual bit
166 		int len = text.length();
167 		byte charType;
168 		for (int i = 0; i < len; i++) {
169 			if (!hasCachedTypeAt(i)) {
170 				charType = Character.getDirectionality(text.charAt(i));
171 				if (charType == B) // B char resolves to L or R depending on orientation
172 					continue;
173 				setBidiTypeAt(i, charType);
174 			} else
175 				charType = getCachedTypeAt(i);
176 			if (charType == L)
177 				return StructuredTextEnvironment.ORIENT_LTR;
178 			if (charType == R || charType == AL)
179 				return StructuredTextEnvironment.ORIENT_RTL;
180 		}
181 		return orient;
182 	}
183 
184 }
185