blob: 53f4d9c3bab2d05031dfe3a91bbaf31e8d021371 [file] [log] [blame]
/*******************************************************************************
* 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$
}
}