| /******************************************************************************* |
| * Copyright (c) 2010, 2011 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.equinox.bidi.custom; |
| |
| import org.eclipse.equinox.bidi.advanced.IStructuredTextExpert; |
| import org.eclipse.equinox.bidi.advanced.StructuredTextEnvironment; |
| import org.eclipse.equinox.bidi.internal.StructuredTextImpl; |
| |
| /** |
| * Generic handler to be used as superclass (base class) |
| * for specific structured text handlers. |
| * <p> |
| * Here are some guidelines about how to write structured text |
| * handlers. |
| * <ul> |
| * <li>Handler instances may be accessed simultaneously by |
| * several threads. They should have no instance variables.</li> |
| * <li>This class provides common logic in code which can be invoked |
| * by any {@link StructuredTextTypeHandler structured text handler}. |
| * This common logic uses handler methods to query the |
| * characteristics of the specific handler: |
| * <ul> |
| * <li>the separators which separate the structured text into |
| * tokens. See {@link #getSeparators}.</li> |
| * <li>the direction which governs the display of tokens |
| * one after the other. See {@link #getDirection}.</li> |
| * <li>the number of special cases which need to be handled by |
| * code specific to that handler. |
| * See {@link #getSpecialsCount}.</li> |
| * </ul></li> |
| * <li>Before starting deeper analysis of the submitted text, the common |
| * logic gives to the handler a chance to shorten the process by |
| * invoking its {@link #skipProcessing} method.</li> |
| * <li>The common logic then analyzes the text to segment it into tokens |
| * according to the appearance of separators (as retrieved using |
| * {@link #getSeparators}).</li> |
| * <li>If the handler indicated a positive number of special cases as |
| * return value from its {@link #getSpecialsCount} |
| * method, the common logic will repeatedly invoke the handler's |
| * {@link #indexOfSpecial} method to let it signal the |
| * presence of special strings which may further delimit the source text.</li> |
| * <li>When such a special case is signaled by the handler, the common |
| * logic will call the handler's {@link #processSpecial} |
| * method to give it the opportunity to handle it as needed. Typical |
| * actions that the handler may perform are to add directional marks |
| * unconditionally (by calling {@link #insertMark} or |
| * conditionally (by calling {@link #processSeparator}).</li> |
| * </ul> |
| */ |
| public class StructuredTextTypeHandler { |
| |
| final private String separators; |
| |
| /** |
| * Creates a new instance of the StructuredTextTypeHandler class. |
| */ |
| public StructuredTextTypeHandler() { |
| separators = ""; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Creates a new instance of the StructuredTextTypeHandler class. |
| * @param separators string consisting of characters that split the text into fragments. |
| */ |
| public StructuredTextTypeHandler(String separators) { |
| this.separators = separators; |
| } |
| |
| /** |
| * Locates occurrences of special strings within a structured text |
| * and returns their indexes one after the other in successive calls. |
| * <p> |
| * This method is called repeatedly if the number of special cases |
| * returned by {@link #getSpecialsCount} is greater than zero. |
| * </p><p> |
| * A handler handling special cases must override this method. |
| * </p> |
| * @param expert IStructuredTextExpert instance through which this handler |
| * is invoked. The handler can use IStructuredTextExpert methods to |
| * query items stored in the expert instance, like the current |
| * {@link StructuredTextEnvironment environment}. |
| * @param text the structured text string before |
| * addition of any directional formatting characters. |
| * @param charTypes an object whose methods can be useful to the |
| * handler. |
| * @param offsets an object whose methods can be useful to the |
| * handler. |
| * @param caseNumber number of the special case to locate. |
| * This number varies from 1 to the number of special cases |
| * returned by {@link #getSpecialsCount} |
| * for this handler. |
| * The meaning of this number is internal to the class |
| * implementing <code>indexOfSpecial</code>. |
| * @param fromIndex the index within <code>text</code> to start |
| * the search from. |
| * |
| * @return the position where the start of the special case |
| * corresponding to <code>caseNumber</code> was located. |
| * The method must return the first occurrence of whatever |
| * identifies the start of the special case starting from |
| * <code>fromIndex</code>. The method does not have to check if |
| * this occurrence appears within the scope of another special |
| * case (e.g. a comment starting delimiter within the scope of |
| * a literal or vice-versa). |
| * <br>If no occurrence is found, the method must return -1. |
| * |
| * @throws IllegalStateException If not overridden, this method throws an |
| * <code>IllegalStateException</code>. This is appropriate behavior |
| * (and does not need to be overridden) for handlers whose |
| * number of special cases is zero, which means that |
| * <code>indexOfSpecial</code> should never be called for them. |
| */ |
| public int indexOfSpecial(IStructuredTextExpert expert, String text, StructuredTextCharTypes charTypes, StructuredTextOffsets offsets, int caseNumber, int fromIndex) { |
| // This method must be overridden by all subclasses with special cases. |
| throw new IllegalStateException("A handler with specialsCount > 0 must have an indexOfSpecial() method."); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Handles special cases specific to this handler. |
| * It is called when a special case occurrence |
| * is located by {@link #indexOfSpecial}. |
| * <p> |
| * If a special processing cannot be completed within a current call to |
| * <code>processSpecial</code> (for instance, a comment has been started |
| * in the current line but its end appears in a following line), |
| * <code>processSpecial</code> should specify a final state by calling |
| * {@link IStructuredTextExpert#setState(Object)}. |
| * The meaning of this state is internal to the handler. |
| * <p> |
| * On a later call, <code>processSpecial</code> will be called with |
| * <code>-1</code> for parameter <code>separLocation</code>. It should then |
| * retrieve the last state by calling {@link IStructuredTextExpert#getState()} and |
| * clear the state by calling {@link IStructuredTextExpert#clearState()}. After that, |
| * it should perform whatever initializations are required |
| * depending on the last state. |
| * </p><p> |
| * A handler handling special cases (with a number of |
| * special cases greater than zero) must override this method. |
| * </p> |
| * @param expert IStructuredTextExpert instance through which this handler |
| * is invoked. The handler can use IStructuredTextExpert methods to |
| * query items stored in the expert instance, like the current |
| * {@link StructuredTextEnvironment environment}. |
| * @param text the structured text string before |
| * addition of any directional formatting characters. |
| * @param charTypes an object whose methods can be useful to the |
| * handler. |
| * @param offsets an object whose methods can be useful to the |
| * handler. |
| * @param caseNumber number of the special case to handle. |
| * @param separLocation the position returned by |
| * {@link #indexOfSpecial}. After calls to |
| * {@link IStructuredTextExpert#leanToFullText} and other |
| * methods of {@link IStructuredTextExpert} which set a non-null |
| * final state, <code>processSpecial</code> is |
| * called when initializing the processing with value of |
| * <code>separLocation</code> equal to <code>-1</code>. |
| * |
| * @return the position after the scope of the special case ends. |
| * For instance, the position after the end of a comment, |
| * the position after the end of a literal. |
| * <br>A value greater or equal to the length of <code>text</code> |
| * means that there is no further occurrence of this case in the |
| * current structured text. |
| * |
| * @throws IllegalStateException If not overridden, this method throws an |
| * <code>IllegalStateException</code>. This is appropriate behavior |
| * (and does not need to be overridden) for handlers whose |
| * number of special cases is zero, which means that |
| * <code>processSpecial</code> should never be called for them. |
| */ |
| public int processSpecial(IStructuredTextExpert expert, String text, StructuredTextCharTypes charTypes, StructuredTextOffsets offsets, int caseNumber, int separLocation) { |
| // This method must be overridden by all subclasses with any special case. |
| throw new IllegalStateException("A handler with specialsCount > 0 must have a processSpecial() method."); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Specifies that a mark character must be added before the character |
| * at the specified position of the <i>lean</i> text when generating the |
| * <i>full</i> text. This method can be called from within |
| * {@link #indexOfSpecial} or |
| * {@link #processSpecial} in extensions of |
| * <code>StructuredTextTypeHandler</code>. |
| * The mark character will be LRM for structured text |
| * with a LTR base direction, and RLM for structured text with RTL |
| * base direction. The mark character is not added physically by this |
| * method, but its position is noted and will be used when generating |
| * the <i>full</i> text. |
| * |
| * @param text is the structured text string received as |
| * parameter to <code>indexOfSpecial</code> or |
| * <code>processSpecial</code>. |
| * @param charTypes is a parameter received by <code>indexOfSpecial</code> |
| * or <code>processSpecial</code>. |
| * @param offsets is a parameter received by <code>indexOfSpecial</code> |
| * or <code>processSpecial</code>. |
| * @param offset position of the character in the <i>lean</i> text. |
| * It must be a non-negative number smaller than the length |
| * of the <i>lean</i> text. |
| * For the benefit of efficiency, it is better to insert |
| * multiple marks in ascending order of the offsets. |
| */ |
| public static final void insertMark(String text, StructuredTextCharTypes charTypes, StructuredTextOffsets offsets, int offset) { |
| offsets.insertOffset(charTypes, offset); |
| } |
| |
| /** |
| * Adds a directional mark before a separator if needed for correct |
| * display, depending on the base direction of the text and on the |
| * class of the characters in the <i>lean</i> text preceding and |
| * following the separator itself. This method |
| * can be called from within {@link #indexOfSpecial} or |
| * {@link #processSpecial} in extensions of |
| * <code>StructuredTextTypeHandler</code>. |
| * <p> |
| * The logic implemented in this method considers the text before |
| * <code>separLocation</code> and the text following it. If, and only if, |
| * a directional mark is needed to insure that the two parts of text |
| * will be laid out according to the base direction, a mark will be |
| * added when generating the <i>full</i> text. |
| * </p> |
| * @param text is the structured text string received as |
| * parameter to <code>indexOfSpecial</code> or |
| * <code>processSpecial</code>. |
| * @param charTypes is a parameter received by <code>indexOfSpecial</code> |
| * or <code>processSpecial</code>. |
| * @param offsets is a parameter received by <code>indexOfSpecial</code> |
| * or <code>processSpecial</code>. |
| * @param separLocation offset of the separator in the <i>lean</i> text. |
| * It must be a non-negative number smaller than the length |
| * of the <i>lean</i> text. |
| */ |
| public static final void processSeparator(String text, StructuredTextCharTypes charTypes, StructuredTextOffsets offsets, int separLocation) { |
| StructuredTextImpl.processSeparator(text, charTypes, offsets, separLocation); |
| } |
| |
| /** |
| * Indicates the separators to use for the current handler. |
| * This method is invoked before starting the processing. |
| * <p> |
| * If no separators are specified, this method returns an empty string. |
| * </p> |
| * @param expert IStructuredTextExpert instance through which this handler |
| * is invoked. The handler can use IStructuredTextExpert methods to |
| * query items stored in the expert instance, like the current |
| * {@link StructuredTextEnvironment environment}. |
| * |
| * @return a string grouping one-character separators which separate |
| * the structured text into tokens. |
| */ |
| public String getSeparators(IStructuredTextExpert expert) { |
| return separators; |
| } |
| |
| /** |
| * Indicates the base text direction appropriate for an instance of |
| * structured text. This method is invoked before starting the processing. |
| * <p> |
| * If not overridden, this method returns {@link IStructuredTextExpert#DIR_LTR DIR_LTR}. |
| * </p> |
| * @param expert IStructuredTextExpert instance through which this handler |
| * is invoked. The handler can use IStructuredTextExpert methods to |
| * query items stored in the expert instance, like the current |
| * {@link StructuredTextEnvironment environment}. |
| * @param text the structured text string to process. |
| * |
| * @return the base direction of the structured text. This direction |
| * may not be the same depending on the environment and on |
| * whether the structured text contains Arabic or Hebrew |
| * letters.<br> |
| * The value returned is either |
| * {@link IStructuredTextExpert#DIR_LTR DIR_LTR} or |
| * {@link IStructuredTextExpert#DIR_RTL DIR_RTL}. |
| */ |
| public int getDirection(IStructuredTextExpert expert, String text) { |
| return IStructuredTextExpert.DIR_LTR; |
| } |
| |
| /** |
| * Indicates the base text direction appropriate for an instance of |
| * structured text. This method is invoked before starting the processing. |
| * <p> |
| * If not overridden, this method returns {@link IStructuredTextExpert#DIR_LTR DIR_LTR}. |
| * </p> |
| * @param expert IStructuredTextExpert instance through which this handler |
| * is invoked. The handler can use IStructuredTextExpert methods to |
| * query items stored in the expert instance, like the current |
| * {@link StructuredTextEnvironment environment}. |
| * @param text is the structured text string to process. |
| * @param charTypes is a parameter received by <code>indexOfSpecial</code> |
| * or <code>processSpecial</code>. |
| * |
| * @return the base direction of the structured text. This direction |
| * may not be the same depending on the environment and on |
| * whether the structured text contains Arabic or Hebrew |
| * letters.<br> |
| * The value returned is either |
| * {@link IStructuredTextExpert#DIR_LTR DIR_LTR} or {@link IStructuredTextExpert#DIR_RTL DIR_RTL}. |
| */ |
| public int getDirection(IStructuredTextExpert expert, String text, StructuredTextCharTypes charTypes) { |
| return IStructuredTextExpert.DIR_LTR; |
| } |
| |
| /** |
| * Indicates the number of special cases handled by the current handler. |
| * This method is invoked before starting the processing. |
| * If the number returned is zero, {@link #indexOfSpecial} |
| * and {@link #processSpecial} will not be invoked. |
| * <p> |
| * If not overridden, this method returns <code>zero</code>. |
| * </p> |
| * @param expert IStructuredTextExpert instance through which this handler |
| * is invoked. The handler can use IStructuredTextExpert methods to |
| * query items stored in the expert instance, like the current |
| * {@link StructuredTextEnvironment environment}. |
| * |
| * @return the number of special cases for the associated handler. |
| * Special cases exist for some types of structured text |
| * handlers. They are implemented by overriding methods |
| * {@link StructuredTextTypeHandler#indexOfSpecial} and |
| * {@link StructuredTextTypeHandler#processSpecial}. |
| * Examples of special cases are comments, literals, or |
| * anything which is not identified by a one-character separator. |
| * |
| */ |
| public int getSpecialsCount(IStructuredTextExpert expert) { |
| return 0; |
| } |
| |
| /** |
| * Checks if there is a need for processing structured text. |
| * This method is invoked before starting the processing. If the |
| * handler returns <code>true</code>, no directional formatting |
| * characters are added to the <i>lean</i> text and the processing |
| * is shortened. |
| * <p> |
| * If not overridden, this method returns <code>false</code>. |
| * </p> |
| * @param expert IStructuredTextExpert instance through which this handler |
| * is invoked. The handler can use IStructuredTextExpert methods to |
| * query items stored in the expert instance, like the current |
| * {@link StructuredTextEnvironment environment}. |
| * @param text is the structured text string to process. |
| * @param charTypes is a parameter received by <code>indexOfSpecial</code> |
| * or <code>processSpecial</code>. |
| * |
| * @return a flag indicating if there is no need to process the structured |
| * text to add directional formatting characters. |
| * |
| */ |
| public boolean skipProcessing(IStructuredTextExpert expert, String text, StructuredTextCharTypes charTypes) { |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| return super.toString() + " [" + separators + "]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |