diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/util/BidiUtils.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/util/BidiUtils.java index 0cdd02f..5c2338e 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/util/BidiUtils.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/util/BidiUtils.java @@ -12,10 +12,13 @@ package org.eclipse.jface.util; import java.util.HashMap; +import java.util.Locale; import java.util.Map; +import org.eclipse.equinox.bidi.StructuredTextProcessor; import org.eclipse.equinox.bidi.StructuredTextTypeHandlerFactory; import org.eclipse.jface.internal.InternalPolicy; +import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BidiSegmentEvent; import org.eclipse.swt.custom.BidiSegmentListener; @@ -90,6 +93,27 @@ public static final String VISUAL_RIGHT_TO_LEFT = "visualrtl";//$NON-NLS-1$ /** + * For applying bidi processing for formatting messages. + * + * @see #applyBidiProcessing(PROCESSING_TYPE, Object...) + */ + public enum PROCESSING_TYPE { + /** + * For applying bidi processing on message placeholders. + * + * @see #applyBidiProcessing(PROCESSING_TYPE, Object...) + */ + MESSAGEWITHPLACEHOLDERS, /** + * For applying bidi processing on + * concatenation of strings. + * + * @see #applyBidiProcessing(PROCESSING_TYPE, + * Object...) + */ + CONCATENATION + } + + /** * Segment listener for LTR Base Text Direction */ private static final SegmentListener BASE_TEXT_DIRECTION_LTR = new BaseTextDirectionSegmentListener(LEFT_TO_RIGHT); @@ -142,6 +166,11 @@ * The RLE char */ static final char RLE = 0x202B; + + /** + * The RLM char + */ + static final char RLM = 0x200F; /** * The LRO char @@ -538,4 +567,199 @@ control.setTextDirection(textDir); } } -} + + /** + * Applies bidi processing to the given string. + * + *

+ * Possible values for handlingType are: + *

+ *

+ * The 3 values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT}, and + * {@link #AUTO} are usable whether {@link #getBidiSupport() bidi support} + * is enabled or disabled. + *

+ * The remaining values only have an effect if bidi support is enabled. + *

+ * The 4 first values {@link #LEFT_TO_RIGHT}, {@link #RIGHT_TO_LEFT}, + * {@link #AUTO}, and {@link #BTD_DEFAULT} are for Base Text Direction (BTD) + * handling. The remaining values are for Structured Text handling. + *

+ * Note: The Structured Text handling only works if the + * org.eclipse.equinox.bidi bundle is on the classpath! + *

+ * Note: To deprocess the string you must use + * {@link BidiUtils#deprocessBidi(String)} for the 6 first values or + * {@link StructuredTextProcessor#deprocessTyped(String, String)} for the + * remaining values. + *

+ * + * @param string + * the string + * @param handlingType + * the type of handling + * @return the bidi processed string + * @throws IllegalArgumentException + * if handlingType is not a known type identifier + */ + public static String applyBidiProcessing(String string, String handlingType) { + if (LEFT_TO_RIGHT.equals(handlingType)) { + return addUCC(string, false, handlingType); + } else if (RIGHT_TO_LEFT.equals(handlingType)) { + return addUCC(string, true, handlingType); + } else if (AUTO.equals(handlingType)) { + return addUCC(string, isRTLValue(string), handlingType); + + } else if (getBidiSupport()) { + if (BTD_DEFAULT.equals(handlingType)) { + if (LEFT_TO_RIGHT.equals(getTextDirection())) { + return addUCC(string, false, handlingType); + } else if (RIGHT_TO_LEFT.equals(getTextDirection())) { + return addUCC(string, true, handlingType); + } else if (AUTO.equals(getTextDirection())) { + return addUCC(string, isRTLValue(string), handlingType); + } + } else if (VISUAL_LEFT_TO_RIGHT.equals(handlingType)) { + return (InternalPolicy.DEBUG_BIDI_UTILS ? handlingType : "") + LRO + string + PDF; //$NON-NLS-1$ + } else if (VISUAL_RIGHT_TO_LEFT.equals(handlingType)) { + return (InternalPolicy.DEBUG_BIDI_UTILS ? handlingType : "") + RLO + string + PDF; //$NON-NLS-1$ + } else { + return StructuredTextProcessor.processTyped(string, handlingType); + } + } + return string; + } + + /** + * Strips off Unicode control characters from the given string. + * + * + * @param string + * the string + * @return the bidi deprocessed string + */ + public static String deprocessBidi(String string) { + String header1 = "" + LRM + LRE, header2 = "" + RLM + RLE; //$NON-NLS-1$ //$NON-NLS-2$ + String tailer1 = "" + LRM + PDF, tailer2 = "" + RLM + PDF; //$NON-NLS-1$ //$NON-NLS-2$ + + if (InternalPolicy.DEBUG_BIDI_UTILS) { + string = string.replaceFirst(LEFT_TO_RIGHT, "").replaceFirst(RIGHT_TO_LEFT, "").replaceFirst(AUTO, "") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + .replaceFirst(BTD_DEFAULT, "").replaceFirst(VISUAL_LEFT_TO_RIGHT, "") //$NON-NLS-1$ //$NON-NLS-2$ + .replaceFirst(VISUAL_RIGHT_TO_LEFT, ""); //$NON-NLS-1$ + } + if ((string.startsWith(header1) && string.endsWith(tailer1)) + || (string.startsWith(header2) && string.endsWith(tailer2))) { + return string.substring(2, string.length() - 2); + } else if ((string.charAt(0) == LRO || string.charAt(0) == RLO) && string.charAt(string.length() - 1) == PDF) { + return string.substring(1, string.length() - 1); + } + return string; + } + + /** + * Applies bidi processing for formatting messages. Note: + * The number of arguments in params should always be even and + * greater than 0. Each pair is composed of the string to bidi process and + * the type of handling as specified in + * {@link #applyBidiProcessing(Text, String)}. If the number of arguments is + * odd no bidi processing will occur. + *

+ * + * @param typeOfContext + * message with placeholders or concatenation ? + * @param params + * unlimited number of arguments associated with either + * placeholders or concatenated strings. It is always even number + * since for each placeholder / piece of text, we need to specify + * which type of handling is required (i.e. text direction + * enforcement, structured text, no handling). + * @return the bidi processed placeholders or the concatenated string. + */ + public static Object[] applyBidiProcessing(PROCESSING_TYPE typeOfContext, Object... params) { + if (typeOfContext == PROCESSING_TYPE.MESSAGEWITHPLACEHOLDERS) { + char prefix = isBidiLocale() ? RLM : LRM; + return applyBidiProcessing(prefix, params); + } else if (typeOfContext == PROCESSING_TYPE.CONCATENATION) { + char prefix = Window.getDefaultOrientation() == SWT.RIGHT_TO_LEFT ? RLM : LRM; + Object[] results = applyBidiProcessing(prefix, params); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < results.length; i++) { + sb.append(results[i]); + } + String[] result = new String[1]; + result[0] = sb.toString(); + return result; + } + return params; + } + + private static Object[] applyBidiProcessing(char prefix, Object... params) { + + if (params.length == 0 || params.length % 2 == 1) { + return params; + } + int numPlaceHolders = params.length / 2; + Object[] results = new Object[numPlaceHolders]; + for (int i = 0; i < numPlaceHolders; i++) { + results[i] = prefix + applyBidiProcessing(params[2 * i].toString(), params[2 * i + 1].toString()); + } + return results; + } + + private static String addUCC(String string, boolean isRTL, String handlingType) { + StringBuffer sb = new StringBuffer(); + if (InternalPolicy.DEBUG_BIDI_UTILS) { + sb.append(handlingType); + } + if (isRTL) { + sb.append(RLM).append(RLE); + } else { + sb.append(LRM).append(LRE); + } + sb.append(string); + if (isRTL) { + sb.append(RLM); + } else { + sb.append(LRM); + } + sb.append(PDF); + return sb.toString(); + } + + private static boolean isRTLValue(String stringValue) { + for (int i = 0; i < stringValue.length(); i++) { + if (Character.getDirectionality(stringValue.charAt(i)) == Character.DIRECTIONALITY_RIGHT_TO_LEFT + || Character + .getDirectionality(stringValue.charAt(i)) == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC + || Character.getDirectionality(stringValue.charAt(i)) == Character.DIRECTIONALITY_ARABIC_NUMBER) + return true; + else if (Character.getDirectionality(stringValue.charAt(i)) == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { + return false; + } + } + return false; + } + + private static boolean isBidiLocale() { + Locale defaultLocale = Locale.getDefault(); + String language = defaultLocale.getLanguage(); + boolean isBidi = "iw".equals(language) || //$NON-NLS-1$ + "he".equals(language) || //$NON-NLS-1$ + "ar".equals(language) || //$NON-NLS-1$ + "fa".equals(language) || //$NON-NLS-1$ + "ur".equals(language); //$NON-NLS-1$ + return isBidi; + } + +} \ No newline at end of file