/******************************************************************************* * 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.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; } public String toString() { return super.toString() + " [" + separators + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } }