com.adobe.xfa.text.TextTabList Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
package com.adobe.xfa.text;
import com.adobe.xfa.ut.UnitSpan;
import java.util.Iterator;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Class TextTabList represents a set of tab stops (see class
* TextTab). Its API is designed to simplify working with the list
* for layout operations.
*
*
* A tab list consists of a set of zero or more individual tab
* stops, as well as an optional uniform tab stop definition.
*
*
* Individual tab stops are ultimately set by the user. They may have
* different types, and their offsets are chosen for visual layout, not
* typically by algorithmic rule. Irrespective of the order individual
* tab stops are added to a tab list, AXTE can infer a graphic order
* from the tab stops' offset values, starting with the smallest and
* ending with the largest.
*
*
* Uniform tab stops kick in after the last individual tab, and are
* offset at regular intervals, theoretically up to infinity. The
* uniform tab stop definition defines that interval through its offset
* and it also defines the tab type for all uniform tabs. Note that
* uniform tab stops are gridded relative to the start of the line, not
* the last individual tab stop.
*
*
* Because the uniform tab stop definition, the application may enable
* or disable it. When enabled, the tab list object effectively
* presents an infinit set of tab stops, starting with the ordered
* individual tab stops, and followed by an ongoing sequence of
* manufactured uniform tab stops. When disabled, the last tab stop in
* the list is the last individual stop.
*
*
* For more information, please see the external documentation.
*
*
* @exclude from published api -- Mike Tardif, May 2006.
*/
// TODO: Check against current implementation.
public class TextTabList {
public final static TextTabList DEFAULT_TAB_LIST = new TextTabList();
private SortedMap moTabs;
private TextTab moUniform;
/**
* Default constructor.
*
* Construct a tab list object with an initially empty set of individual
* tabs and a uniform tab definition of 0.5", left aligned.
*/
public TextTabList () {
moUniform = TextTab.DEFAULT_TAB;
}
/**
* Copy constructor.
*
* Copy the source tab list, including the complete set of individual
* tab stops and the uniform tab stop definition.
* @param oSource - Source tab list to copy.
*/
public TextTabList (TextTabList oSource) {
if (oSource.moTabs != null) {
moTabs = new TreeMap(oSource.moTabs);
}
moUniform = oSource.moUniform;
}
/**
* Obtain the uniform tab stop definition.
* @return Current uniform tab stop definition.
*/
public TextTab uniform () {
return noUniform() ? TextTab.DEFAULT_TAB : moUniform;
}
/**
* Change the uniform tab stop definition.
* @param oNewUniform - New uniform tab stop definition. The call is
* ignored if this has a zero or negative value.
*/
public void uniform (TextTab oNewUniform) {
if (oNewUniform.value() <= 0) {
return;
}
moUniform = oNewUniform;
}
/**
* Query whether the uniform tab stop definition is disabled.
* @return TRUE if the uniform tab stop definition is disabled; FALSE if
* enabled.
*/
public boolean noUniform () {
return moUniform.value() == 0;
}
/**
* Enable/disable the uniform tab stop definition.
* @param bNoUniform - TRUE if the uniform tab stop definition is to be
* disabled; FALSE to enable it.
*/
public void noUniform (boolean bNoUniform) {
if (bNoUniform) {
moUniform = TextTab.ZERO_TAB;
} else if (moUniform.value() == 0) {
moUniform = TextTab.DEFAULT_TAB;
}
}
/**
* Set a new tab stop.
*
* This method adds a new tab stop to the set of individual tabs. The
* new tab stop can then be retrieved in correct sequence with the
* Next() and Prev() methods. Note that there may not be two tab stops
* at the same offset. If the list already contains a tab stop at the
* new tab's offset, the existing tab is replaced.
* @param oSet - New individual tab to add to the set.
*/
public void set (TextTab oSet) {
if (moTabs == null) {
moTabs = new TreeMap();
}
moTabs.put (oSet.tabStop(), oSet);
}
/**
* Clear one tab stop.
*
* If the list contains an individual tab at the offset of the parameter
* passed in, remove it from the list. Otherwise, the call is ignored.
* One cannot clear uniform tab stop instances.
* @param oClear - Tab stop to clear. Only the offset is examined; the
* type is ignored.
*/
public void clear (TextTab oClear) {
if (moTabs != null) {
moTabs.remove (oClear.tabStop()); // removes if found
}
}
/**
* Clear all tabs.
*
* Remove all individual tabs and set the uniform tab stop back to its
* initial definition (0.5" left).
*/
public void clearAll () {
if (moTabs != null) {
moTabs.clear();
}
moUniform = TextTab.DEFAULT_TAB;
}
/**
* Obtain the number of individual tab stops.
* @return Number of individual tab stops currently in the list. Not
* influenced by the state of the uniform tab stop definition.
*/
public int size () {
return (moTabs == null) ? 0 : moTabs.size();
}
/**
* Obtain the next tab.
*
* Given an offset measurement, this method returns the next tab from
* the list. Note that a tab exactly at the given offset is not a
* suitable candidate. If uniform tabs are enabled and the offset is
* beyond the last individual tab stop, the appropriate uniform tab stop
* is returned. If the offset is beyond the last individual tab stop
* and uniform tabs are disabled, a tab with a zero offset is returned.
* @param oPosition - Offset to start the search from.
* @return Resulting tab stop. Note: This is currently a const
* reference to static storage ans is not thread safe. Must change to
* return by value as part of the HATV effort.
*/
public TextTab next (UnitSpan oPosition) {
// First, search the tab list for a specific tab after the given
// position.
if (moTabs != null) {
for (TextTab textTab : moTabs.values()) {
if (textTab.tabStop().compareTo(oPosition) > 0)
return textTab;
}
}
// Didn't find it: are there no uniform tabs?
if (noUniform()) {
return TextTab.ZERO_TAB;
// Compute the next uniform tab.
} else {
UnitSpan oValue = oPosition.grid (moUniform.tabStop()); // grid *down* always
oValue = oValue.add (moUniform.tabStop()); // next grid
return new TextTab (oValue, moUniform.tabType()); // construct a tab stop
}
}
/**
* Obtain the previous tab.
*
* Given an offset measurement, this method returns the previous tab
* from the list. Note that a tab exactly at the given offset is not a
* suitable candidate. If uniform tabs are enabled and the offset is
* far enough beyond the last individual tab stop, the appropriate
* uniform tab stop is returned. If the offset is beyond the last
* individual tab stop and uniform tabs are disabled, the last
* individual tab stop is returned.
* @param oPosition - Offset to start the search from.
* @return Resulting tab stop. Note: This is currently a const
* reference to static storage ans is not thread safe. Must change to
* return by value as part of the HATV effort.
*/
public TextTab prev (UnitSpan oPosition) {
if (oPosition.value() <= 0) {
// Never try to have a tab below zero.
return TextTab.ZERO_TAB;
}
if (moTabs == null) {
return TextTab.ZERO_TAB;
}
// If there is a tab list, see if we are to end up before the last tab in
// the list. If so, return the appropriate tab from the list.
TextTab oLastTab = TextTab.ZERO_TAB;
if (moTabs.size() > 0) {
oLastTab = moTabs.get(moTabs.lastKey());
if (oPosition.lte (oLastTab.tabStop())) { // at/before last tab
UnitSpan previous = null;
for (UnitSpan currentPosition : moTabs.keySet()) {
if (oPosition.compareTo(currentPosition) >= 0) {
if (previous == null)
return TextTab.ZERO_TAB;
else
return moTabs.get(previous);
}
previous = currentPosition;
}
}
}
if (noUniform()) {
// We're beyond the last tab. If no uniform tabs, use the last one.
return oLastTab;
}
// Otherwise, construct the appropriate uniform position and see if we
// should use it.
UnitSpan oUniform = oPosition.grid (moUniform.tabStop()); // grid *down*
if (oUniform.equals (oPosition)) { // if wasn't changed ...
oUniform = oUniform.subtract (moUniform.tabStop()); // ... force down by grid
}
// If there is a tab list and the appropriate uniform tab is before the
// end of the list, use the last tab.
if ((moTabs.size() > 0) && (oLastTab.tabStop().gte (oUniform))) {
return oLastTab;
}
// Otherwise, tab back to the previous uniform tab.
return new TextTab (oUniform, moUniform.tabType());
}
/**
* Assignment operator.
*
* Copy the source tab list, including the complete set of individual
* tab stops and the uniform tab stop definition.
* @param oSource - Source tab list to copy.
*/
public void copyFrom (TextTabList oSource) {
moUniform = oSource.moUniform;
if (oSource.moTabs == null) {
moTabs = null;
} else {
moTabs = new TreeMap(oSource.moTabs);
}
}
/**
* Equality comparison.
*
* Two tab lists are considered equal if they have the same sets of tab
* stops and the same uniform tab stop definition. Tab stops are
* compared by both offset and type. The uniform tab stop definitions
* are equal if they are both disabled or they are both enabled and
* represent the same tab stop.
* @param object - Tab list to compare against.
* @return TRUE if the tab lists are equal; FALSE otherwise.
*/
public boolean equals (Object object) {
if (this == object)
return true;
// This overrides Object.equals(boolean) directly, so...
if (object == null)
return false;
if (object.getClass() != getClass())
return false;
TextTabList compare = (TextTabList) object;
if (! moUniform.equals (compare.moUniform))
return false;
if (size() != compare.size())
return false;
if (size() == 0)
return true;
// Note change from C++: sufficient to compare map values,
// because map keys are derived from the values.
Iterator it1 = moTabs.values().iterator();
Iterator it2 = compare.moTabs.values().iterator();
while (it1.hasNext()) {
if (!it1.next().equals(it2.next()))
return false;
}
return true;
}
public int hashCode() {
int hash = 71;
hash = (hash * 31) ^ moUniform.hashCode();
if (moTabs != null)
for (TextTab textTab : moTabs.values())
hash = (hash * 31) ^ textTab.hashCode();
return hash;
}
/**
* Inequality comparison.
*
* Two tab lists are considered unequal if they have different sets of
* tab stops or different uniform tab stop definitions. Tab stops are
* compared by both offset and type. The uniform tab stop definitions
* are unequal if their enabled/disabled states don't match or they are
* both enabled and represent different tab stops.
* @param oCompare - Tab list to compare against.
* @return TRUE if the tab lists are not equal; FALSE otherwise.
*/
public boolean notEqual (TextTabList oCompare) {
return ! equals (oCompare);
}
/**
* Tab stop indexing.
*
* Return one tab stop from the list, accessed by index number. This
* method treats the tab list as an ordered list of individual tab stops
* followed by an open-ended list of uniform tabs, if enabled. The
* caller can request any tab stop from this view.
* @param nIndex - Index number of desired tab stop.
* @return Resulting tab stop. Note: This is currently a const
* reference to static storage ans is not thread safe. Must change to
* return by value as part of the HATV effort.
*/
public TextTab tabAt (int nIndex) {
if (nIndex == 0) {
return TextTab.ZERO_TAB; // "real" indexes start at 1
}
int nTabs = size();
if (nIndex <= nTabs) { // within tab list ...
return getTab (nIndex - 1); // ... "real" indexes start at 1
}
// If we're at this point, we need to return a uniform tab (beyond the
// end of the tab list). Start by determining the position of the next
// uniform tab.
UnitSpan oValue = null;
if (nTabs > 0) {
oValue = moTabs.lastKey();
}
oValue = next(oValue).tabStop(); // now at next uniform tab.
// Determine how many more uniform tab stops to go from oValue.
int lOffset = nIndex - nTabs - 1;
// Compute a new tab stop at the given offset.
UnitSpan stop = moUniform.tabStop();
stop = stop.multiply(lOffset);
stop = stop.add(oValue);
return new TextTab (stop, moUniform.tabType());
}
public void debug () {
debug (0);
}
void debug (int indent) {
String prefix = Pkg.doIndent (indent+1);
System.out.println (prefix + "Tab list:");
prefix += ' ';
indent += 2;
if (size() > 0) {
System.out.println (prefix + "Tab stops:");
for (TextTab textTab : moTabs.values()) {
textTab.debug (indent);
}
}
if (! noUniform()) {
System.out.println (prefix + "Uniform:");
uniform().debug (indent);
}
}
private final TextTab getTab (int index) {
int i = 0;
for (TextTab textTab : moTabs.values()) {
if (i == index)
return textTab;
i++;
}
return null;
}
}