| /******************************************************************************* |
| * Copyright (c) 2010, 2011 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.equinox.bidi; |
| |
| import org.eclipse.equinox.bidi.custom.BidiComplexProcessor; |
| |
| /** |
| * This class provides a number of convenience functions facilitating the |
| * processing of complex expressions. |
| * |
| * @noextend This class is not intended to be subclassed by clients. |
| * @noinstantiate This class is not intended to be instantiated by clients. |
| * |
| * @author Matitiahu Allouche |
| */ |
| final public class BidiComplexUtil { |
| |
| /** |
| * prevent instantiation |
| */ |
| private BidiComplexUtil() { |
| // empty |
| } |
| |
| /** This is a convenience method which can add directional marks in a given |
| * text before the characters specified in the given array of offsets, |
| * and can add a prefix and/or a suffix of directional formatting characters. |
| * This can be used for instance after obtaining offsets by calling |
| * {@link BidiComplexHelper#leanBidiCharOffsets() leanBidiCharOffsets} in order to |
| * produce a <i>full</i> text corresponding to the source text. |
| * The directional formatting characters that will be added at the given |
| * offsets will be LRMs for expressions with LTR base direction and |
| * RLMs for expressions with RTL base direction. Leading and |
| * trailing LRE, RLE and PDF which might be needed as prefix or suffix |
| * depending on the orientation of the GUI component used for display |
| * may be added depending on argument <code>affix</code>. |
| * |
| * @param text is the text of the complex expression. |
| * |
| * @param offsets is an array of offsets to characters in <code>text</code> |
| * before which an LRM or RLM will be inserted. |
| * Members of the array must be non-negative numbers smaller |
| * than the length of <code>text</code>. |
| * The array must be sorted in ascending order without duplicates. |
| * This argument may be null if there are no marks to add. |
| * |
| * @param direction specifies the base direction of the complex expression. |
| * It must be one of the values {@link BidiComplexFeatures#DIR_LTR} or |
| * {@link BidiComplexFeatures#DIR_RTL}. |
| * |
| * @param affix specifies if a prefix and a suffix should be added to |
| * the result to make sure that the <code>direction</code> |
| * specified as third argument is honored even if the expression |
| * is displayed in a GUI component with a different orientation. |
| * |
| * @return a string corresponding to the source <code>text</code> with |
| * directional marks (LRMs or RLMs) added at the specified offsets, |
| * and directional formatting characters (LRE, RLE, PDF) added |
| * as prefix and suffix if so required. |
| */ |
| public static String insertMarks(String text, int[] offsets, int direction, boolean affix) { |
| int textLen = text.length(); |
| if (textLen == 0) |
| return ""; //$NON-NLS-1$ |
| |
| String curPrefix, curSuffix, full; |
| char curMark, c; |
| char[] fullChars; |
| if (direction == BidiComplexFeatures.DIR_LTR) { |
| curMark = LRM; |
| curPrefix = "\u202a\u200e"; /* LRE+LRM *///$NON-NLS-1$ |
| curSuffix = "\u200e\u202c"; /* LRM+PDF *///$NON-NLS-1$ |
| } else { |
| curMark = RLM; |
| curPrefix = "\u202b\u200f"; /* RLE+RLM *///$NON-NLS-1$ |
| curSuffix = "\u200f\u202c"; /* RLM+PDF *///$NON-NLS-1$ |
| } |
| // add marks at offsets |
| if ((offsets != null) && (offsets.length > 0)) { |
| int offLen = offsets.length; |
| fullChars = new char[textLen + offLen]; |
| int added = 0; |
| for (int i = 0, j = 0; i < textLen; i++) { |
| c = text.charAt(i); |
| if ((j < offLen) && (i == offsets[j])) { |
| fullChars[i + added] = curMark; |
| added++; |
| j++; |
| } |
| fullChars[i + added] = c; |
| } |
| full = new String(fullChars); |
| } else { |
| full = text; |
| } |
| if (affix) |
| return curPrefix + full + curSuffix; |
| return full; |
| } |
| |
| /*************************************************************************/ |
| /* */ |
| /* The following code is provided for compatibility with TextProcessor */ |
| /* */ |
| /*************************************************************************/ |
| |
| // The default set of delimiters to use to segment a string. |
| private static final String defaultDelimiters = ".:/\\"; //$NON-NLS-1$ |
| // left to right mark |
| private static final char LRM = '\u200e'; |
| // left to right mark |
| private static final char RLM = '\u200f'; |
| // left to right embedding |
| private static final char LRE = '\u202a'; |
| // right to left embedding |
| private static final char RLE = '\u202b'; |
| // pop directional format |
| private static final char PDF = '\u202c'; |
| |
| static boolean isProcessingNeeded() { |
| if (!BidiComplexEnvironment.isSupportedOS()) |
| return false; |
| return BidiComplexEnvironment.DEFAULT.isBidi(); |
| } |
| |
| /** |
| * Process the given text and return a string with appropriate |
| * directional formatting characters if the locale is a bidi locale. |
| * This is equivalent to calling |
| * {@link #process(String str, String delimiters)} with the default |
| * set of delimiters (dot, colon, slash, backslash). |
| * |
| * @param str the text to be processed. |
| * |
| * @return the processed string. |
| */ |
| public static String process(String str) { |
| return process(str, defaultDelimiters); |
| } |
| |
| /** |
| * Process a string that has a particular semantic meaning to render |
| * it correctly on bidi locales. This is done by adding directional |
| * formatting characters so that presentation using the Unicode |
| * Bidirectional Algorithm will provide the expected result. |
| * The text is segmented according to the provided delimiters. |
| * Each segment has the Unicode Bidi Algorithm applied to it, |
| * but as a whole, the string is oriented left to right. |
| * <p> |
| * For example, a file path such as <tt>d:\myfolder\FOLDER\MYFILE.java</tt> |
| * (where capital letters indicate RTL text) should render as |
| * <tt>d:\myfolder\REDLOF\ELIFYM.java</tt>.</p> |
| * <p> |
| * NOTE: this method inserts directional formatting characters into the |
| * text. Methods like <code>String.equals(String)</code> and |
| * <code>String.length()</code> called on the resulting string will not |
| * return the same values as would be returned for the original string.</p> |
| * |
| * @param str the text to process. |
| * |
| * @param delimiters delimiters by which the string will be segmented. |
| * If <code>null</code>, the default delimiters are used |
| * (dot, colon, slash, backslash). |
| * |
| * @return the processed string. |
| * If <code>str</code> is <code>null</code>, |
| * or of length 0, or if the current locale is not a bidi one, |
| * return the original string. |
| */ |
| public static String process(String str, String delimiters) { |
| if ((str == null) || (str.length() <= 1) || !isProcessingNeeded()) |
| return str; |
| |
| // do not process a string that has already been processed. |
| if (str.charAt(0) == LRE && str.charAt(str.length() - 1) == PDF) |
| return str; |
| |
| // do not process a string if all the following conditions are true: |
| // a) it has no RTL characters |
| // b) it starts with a LTR character |
| // c) it ends with a LTR character or a digit |
| boolean isStringBidi = false; |
| int strLength = str.length(); |
| char c; |
| for (int i = 0; i < strLength; i++) { |
| c = str.charAt(i); |
| if (((c >= 0x05d0) && (c <= 0x07b1)) || ((c >= 0xfb1d) && (c <= 0xfefc))) { |
| isStringBidi = true; |
| break; |
| } |
| } |
| while (!isStringBidi) { |
| if (!Character.isLetter(str.charAt(0))) |
| break; |
| c = str.charAt(strLength - 1); |
| if (!Character.isDigit(c) && !Character.isLetter(c)) |
| break; |
| return str; |
| } |
| |
| if (delimiters == null) |
| delimiters = defaultDelimiters; |
| |
| // make sure that LRE/PDF are added around the string |
| BidiComplexEnvironment env = new BidiComplexEnvironment(null, false, BidiComplexEnvironment.ORIENT_UNKNOWN); |
| BidiComplexHelper helper = new BidiComplexHelper(new BidiComplexProcessor(), env); |
| helper.setFeatures(new BidiComplexFeatures(delimiters, 0, -1, -1, false, false)); |
| return helper.leanToFullText(str); |
| } |
| |
| /** |
| * Process a string that has a particular semantic meaning to render |
| * it correctly on bidi locales. This is done by adding directional |
| * formatting characters so that presentation using the Unicode |
| * Bidirectional Algorithm will provide the expected result.. |
| * The text is segmented according to the syntax specified in the |
| * <code>type</code> argument. |
| * Each segment has the Unicode Bidi Algorithm applied to it, but the |
| * order of the segments is governed by the type of the complex expression. |
| * <p> |
| * For example, a file path such as <tt>d:\myfolder\FOLDER\MYFILE.java</tt> |
| * (where capital letters indicate RTL text) should render as |
| * <tt>d:\myfolder\REDLOF\ELIFYM.java</tt>.</p> |
| * <p> |
| * NOTE: this method inserts directional formatting characters into the |
| * text. Methods like <code>String.equals(String)</code> and |
| * <code>String.length()</code> called on the resulting string will not |
| * return the same values as would be returned for the original string.</p> |
| * |
| * @param str the text to process. |
| * |
| * @param type specifies the type of the complex expression. It must |
| * be one of the values in {@link IBidiComplexExpressionTypes} or a value. |
| * added by a plug-in extension. |
| * |
| * @return the processed string. |
| * If <code>str</code> is <code>null</code>, |
| * or of length 0, or if the current locale is not a bidi one, |
| * return the original string. |
| */ |
| public static String processTyped(String str, String type) { |
| if ((str == null) || (str.length() <= 1) || !isProcessingNeeded()) |
| return str; |
| |
| // do not process a string that has already been processed. |
| char c = str.charAt(0); |
| if (((c == LRE) || (c == RLE)) && str.charAt(str.length() - 1) == PDF) |
| return str; |
| |
| // make sure that LRE/PDF are added around the string |
| BidiComplexEnvironment env = new BidiComplexEnvironment(null, false, BidiComplexEnvironment.ORIENT_UNKNOWN); |
| BidiComplexHelper helper = new BidiComplexHelper(type, env); |
| return helper.leanToFullText(str); |
| } |
| |
| /** |
| * Remove directional formatting characters in the given string that |
| * were inserted by one of the {@link #process process} methods. |
| * |
| * @param str string with directional characters to remove. |
| * |
| * @return string with no directional formatting characters. |
| */ |
| public static String deprocess(String str) { |
| if ((str == null) || (str.length() <= 1) || !isProcessingNeeded()) |
| return str; |
| |
| StringBuffer buf = new StringBuffer(); |
| int strLen = str.length(); |
| for (int i = 0; i < strLen; i++) { |
| char c = str.charAt(i); |
| switch (c) { |
| case LRM : |
| continue; |
| case LRE : |
| continue; |
| case PDF : |
| continue; |
| default : |
| buf.append(c); |
| } |
| } |
| return buf.toString(); |
| } |
| |
| /** |
| * Remove directional formatting characters in the given string that |
| * were inserted by the {@link #processTyped processTyped} method. |
| * |
| * @param str string with directional characters to remove. |
| * |
| * @param type type of the complex expression as specified when |
| * calling {@link #processTyped processTyped}. |
| * |
| * @return string with no directional formatting characters. |
| */ |
| public static String deprocess(String str, String type) { |
| if ((str == null) || (str.length() <= 1) || !isProcessingNeeded()) |
| return str; |
| |
| // make sure that LRE/PDF are added around the string |
| BidiComplexEnvironment env = new BidiComplexEnvironment(null, false, BidiComplexEnvironment.ORIENT_UNKNOWN); |
| BidiComplexHelper helper = new BidiComplexHelper(type, env); |
| return helper.fullToLeanText(str); |
| } |
| |
| } |