com.adobe.xfa.text.DispRaw Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
package com.adobe.xfa.text;
import com.adobe.xfa.font.FontInstance;
import com.adobe.xfa.font.FontItem;
import com.adobe.xfa.gfx.GFXGlyphOrientation;
/**
* @exclude from published api.
*/
class RawProcessor {
//----------------------------------------------------------------------
//
// DispRawProcessor - Actual processing of raw data to
// populate a raw line.
//
//----------------------------------------------------------------------
private final DispLineRaw mpoLine;
private final FormatInfo moFormatInfo;
private final DispMapSpan moAnchor;
private final DispMapSpan moRun;
private TextAttr mpoPendingAttr;
private int mnVisualCharCount;
private boolean mbHasAmbiguousBreaks;
private int[] moAccentRun;
int mnAccentRunSize;
private int meAccentRunBreak;
private int meGlyphOrientation;
// private boolean mbFit; // TODO: not referenced
private static class ValidationData {
TextAttr originalAttr;
FontInstance originalFontInstance;
FontInstance fontInstance;
boolean replaceFont;
}
RawProcessor (FormatInfo oFormatInfo, DispLineRaw poLine, TextPosnBase oInitialPosition, TextAttr poInitialAttr) {
mpoLine = poLine;
moFormatInfo = oFormatInfo;
moAnchor = new DispMapSpan (poLine, new DispPosn (oInitialPosition));
moRun = new DispMapSpan (poLine, new DispRun (poLine.frame(), poInitialAttr));
mnVisualCharCount = 0;
mbHasAmbiguousBreaks = false;
moAccentRun = poLine.display().getContext().getAccentRun (0);
meAccentRunBreak = 0;
meGlyphOrientation = GFXGlyphOrientation.HORIZONTAL;
}
void processAttr (TextAttr poAttr) {
flushAccentRun();
poAttr = moFormatInfo.resolveAttr (poAttr);
moFormatInfo.setAttr (poAttr);
if ((poAttr != null) /* && poAttr.fontEnable() */ && poAttr.substituteFont()) {
mpoLine.display().setFontSubstitution (true);
}
DispRun oNewRun = new DispRun (moRun.r(), moFormatInfo.getAttr());
moRun.reset (oNewRun);
mpoPendingAttr = null;
}
void processChar (int c, int eBreak, TextPosnBase poPosition) {
if (mpoLine.frame().isOrientationVertical()) {
int eGlyphOrientation = figureVerticalOrientationsFromChar (c);
if (meGlyphOrientation != eGlyphOrientation) {
flushAccentRun();
DispRun oNewRun = new DispRun (moRun.r(), eGlyphOrientation);
moRun.reset (oNewRun);
meGlyphOrientation = eGlyphOrientation;
}
}
// Tab character: Embed a an object of type DispTab in the line at
// the character index of the tab. The size of this object will be
// filled in during word-wrapping.
// TBD: what if tab in decomposition run?
if (c == '\t') {
syncPosition (poPosition);
DispTab poTab = new DispTab();
TextAttr poAttr = moRun.r().getAttr();
if (poAttr != null) {
poTab.setHeight (poAttr.size());
}
DispEmbed poDispEmbed = addEmbed (poTab, '\t', TextCharProp.defaultSpace); // TODO: C++ implementation doesn't use complete set of properties
if (poDispEmbed == null) {
return;
}
DispTab poMappedTab = (DispTab) poDispEmbed.getEmbed();
moFormatInfo.addTab (poMappedTab);
}
// Watson #1245453 - don't allow Unicode direction change characters
// to be added to moAccentRun, as they can cause unwanted font changes
else if ((c >= 0x202A) && (c <= 0x202F)) {
syncPosition (poPosition);
addChar (c, eBreak);
}
// Normal character: Accumulate each sequence of base character plus zero
// or more accents into a run that can be processed as a unit.
else {
if (TextCharProp.getBreakClass (eBreak) != TextCharProp.BREAK_CM) {
syncPosition (poPosition); // flushes current accent run and sets position
meAccentRunBreak = eBreak;
}
int newSize = mnAccentRunSize + 1;
if (newSize >= moAccentRun.length) {
moAccentRun = mpoLine.display().getContext().getAccentRun (newSize);
}
moAccentRun[mnAccentRunSize] = c; // TODO: rethink handling of accent run on Java
mnAccentRunSize = newSize;
}
}
void processObject (TextEmbed poEmbed, TextPosnBase poPosition) {
syncPosition (poPosition);
addEmbed (poEmbed);
}
void processPara (TextPosnBase poPosition) {
syncPosition (poPosition);
addChar (' ', TextCharProp.defaultSpace); // treat as new-line
mpoPendingAttr = null;
moFormatInfo.setNewPara (true);
}
void processStreamStart (TextPosnBase oPosition) {
processStreamChange (oPosition);
}
void processStreamEnd (TextPosnBase oPosition) {
processStreamChange (oPosition);
}
void finish (boolean bIsLastParaLine, boolean bIsLastLineInStream) {
flushAccentRun();
moAnchor.flush();
moRun.flush();
if (bIsLastParaLine) {
mpoLine.setLastParaLine (DispLine.REAL_LAST_LINE);
} else {
mpoLine.setLastParaLine (DispLine.HARD_NEW_LINE);
}
mpoLine.setLastLineInStream (bIsLastLineInStream);
mpoLine.setVisualCharCount (mnVisualCharCount);
if (mbHasAmbiguousBreaks) {
int eDefault = mpoLine.display().isIdeographic() ? TextCharProp.BREAK_ID : TextCharProp.BREAK_AL;
int ePrev = TextCharProp.WIDTH_A;
int eNext = TextCharProp.WIDTH_A;
int nNextKnown = 0;
for (int i = 0; i < mpoLine.getCharCount(); i++) {
int nData = mpoLine.getBreakData (i);
int eBreak = TextCharProp.getBreakClass (nData);
int eWidth = TextCharProp.getWidthClass (nData);
if (eBreak == TextCharProp.BREAK_AI) {
eBreak = TextCharProp.resolveBreakWidth (eWidth, eWidth);
}
if (eBreak == TextCharProp.BREAK_AI) {
if (nNextKnown <= i) {
nNextKnown = i + 1;
eNext = TextCharProp.WIDTH_A;
while ((eNext == TextCharProp.WIDTH_A) && (nNextKnown < mpoLine.getCharCount())) {
int eNextWidth = mpoLine.getWidthClass (nNextKnown);
nNextKnown++;
if (! TextCharProp.isAmbiguousWidth (eNextWidth)) {
eNext = eWidth;
break;
}
}
}
eBreak = TextCharProp.resolveBreakWidth (ePrev, eNext, eDefault);
mpoLine.setBreak (i, TextCharProp.setBreakClass (nData, eBreak));
if (eBreak == TextCharProp.BREAK_ID) {
ePrev = TextCharProp.WIDTH_W;
} else {
ePrev = TextCharProp.WIDTH_N;
}
}
else {
if (! TextCharProp.isAmbiguousWidth (eWidth)) {
ePrev = eWidth;
}
}
}
}
}
private void processStreamChange (TextPosnBase oPosition) {
flushAccentRun();
moAnchor.reset (new DispPosn (oPosition), true);
TextAttr poAttr = moAnchor.pp().attributePtr();
if ((poAttr == null) || (poAttr.fontInstance() == null)) {
TextAttr poRunAttr = moRun.r().getAttr();
if (poRunAttr == null) {
poRunAttr = poAttr;
}
assert (poRunAttr != null);
TextAttr poNewAttr = new TextAttr (true);
poNewAttr.fontService (poRunAttr.fontService());
poNewAttr.override (poRunAttr);
poAttr = poNewAttr;
}
moFormatInfo.setAttr (poAttr);
moRun.reset (new DispRun (moRun.r(), moFormatInfo.getAttr()));
mpoPendingAttr = null;
}
private void syncPosition (TextPosnBase poPosition) {
flushAccentRun();
if (poPosition != null) {
if ((moAnchor.pp().stream() != poPosition.stream())
|| ((moAnchor.pp().index() + moAnchor.length()) != poPosition.index())) {
moAnchor.reset (new DispPosn (poPosition));
}
}
}
private DispEmbed addEmbed (TextEmbed poEmbed, int cInsert, int eBreak) {
moRun.flush();
int nCharsBefore = mpoLine.getCharCount();
if (cInsert == '\t') {
assert (mnAccentRunSize == 0);
if ((moAccentRun == null) || (moAccentRun.length < 1)) {
moAccentRun = mpoLine.display().getContext().getAccentRun (1);
}
moAccentRun[0] = cInsert; // TODO: rethink handling of accent run on Java
mnAccentRunSize = 1;
resolveAccentRunFont();
mnAccentRunSize = 0;
}
addChar (cInsert, eBreak);
DispEmbed oDispEmbed = new DispEmbed (poEmbed);
mpoLine.add (oDispEmbed, nCharsBefore);
moRun.flush(); // each embedded obj has its own run for consistency with C++ implementation
return oDispEmbed;
}
private DispEmbed addEmbed (TextEmbed poEmbed) {
return addEmbed (poEmbed, Pkg.EMBED_OBJ_CHAR, TextCharProp.defaultObject);
}
private void flushAccentRun () {
// This method flushes any run of base character plus zero or more
// accents to the line.
if (mnAccentRunSize == 0) {
return;
}
// Resolve the fonts for the accent run. This also returns the number of
// characters collapsed for AXTE ligatures.
int nComposed = resolveAccentRunFont();
int c = moAccentRun[0];
// Certain languages require special run breaks and properties. Arabic
// requires common ligatures be enabled while Thai requires kerning. In
// addition, Arabic requires special handling for comb fields. Use
// character ranges to determine Arabic language, since we don't track it
// as a separate character property.
boolean bIsArabic = false;
boolean bIsThai = false;
if (((c >= 0x0600) && (c <= 0x074F)) // Arabic, Arabic supplement, Syriac
|| ((c >= 0x0780) && (c <= 0x07BF)) // Thaana
|| ((c >= 0xFB50) && (c <= 0xFDFF)) // Arabic presentation forms A
|| ((c >= 0xFE70) && (c <= 0xFEFF))) { // Arabic presentation forms B
bIsArabic = true;
} else if ((c >= 0x0E00) && (c <= 0x0E7F)) { // Thai
bIsThai = true;
}
// If this is a comb field, force a new run on every Arabic base
// character. This is in case the text is Arabic; we want all characters
// in isolated form and they must be in separate runs for this to happen.
if (mpoLine.getCombCells() > 0) {
if (bIsArabic) {
DispRun oNewRun = new DispRun (moRun.r());
oNewRun.setComb (true);
moRun.reset (oNewRun);
}
}
// Otherwise, use character's language to determine whether common
// ligatures or kerning are required or not and recycle the current run
// if necessary.
else {
boolean bAllowLigatures = bIsArabic;
boolean bAllowKerning = bIsThai;
TextAttr poAttr = moRun.r().getAttr();
if (poAttr != null) {
if (poAttr.ligatureEnable() && (poAttr.ligature() == TextAttr.LIGATURE_COMMON)) {
bAllowLigatures = true;
}
if (poAttr.kerningEnable() && poAttr.kerning()) {
bAllowKerning = true;
}
}
if ((bAllowLigatures != moRun.r().allowLigatures()) || (bAllowKerning != moRun.r().allowKerning())) {
DispRun oNewRun = new DispRun (moRun.r());
oNewRun.setAllowLigatures (bAllowLigatures);
oNewRun.setAllowKerning (bAllowKerning);
moRun.reset (oNewRun);
}
}
// If there is an AXTE ligature, create a new position map entry that
// represents the composition, and flush the old entry.
if (nComposed > 1) {
DispPosn p = moAnchor.p();
TextPosn pp = p.pp();
int nAnchorStreamCount = (p.getStreamCount() == 0) ? moAnchor.length() : p.getStreamCount();
DispPosn oNewPosn = new DispPosn (pp.stream(), pp.index() + nAnchorStreamCount, pp.position());
oNewPosn.setStreamCount (nComposed);
moAnchor.reset (oNewPosn);
}
// We're now ready to add the character to the line.
addChar (c, meAccentRunBreak);
int eClass = TextCharProp.getBreakClass (meAccentRunBreak);
if (eClass != TextCharProp.BREAK_SP) {
if (eClass == TextCharProp.BREAK_AI) {
mbHasAmbiguousBreaks = true;
}
mnVisualCharCount = mpoLine.getCharCount();
}
// Run through any accents that follow the base character and add them as
// well.
for (int nAccentIndex = 1; nAccentIndex < mnAccentRunSize; nAccentIndex++) {
int cAccent = moAccentRun[nAccentIndex];
if (cAccent != '\0') {
addChar (cAccent, TextCharProp.getCharProperty (cAccent));
}
mnVisualCharCount = mpoLine.getCharCount();
}
// Set up for next time.
mnAccentRunSize = 0;
moFormatInfo.setNewPara (false);
}
private int resolveAccentRunFont () {
// Confirm that this base/accent run is in the current font. If not, try
// to find a font that does work.
// First, resolve a change to base character if the text is invisible.
int nComposed = 1;
int c = substituteInvisibleCharacter (moAccentRun[0]);
if (c != moAccentRun[0]) {
moAccentRun[0] = c;
meAccentRunBreak = TextCharProp.getCharProperty (c);
}
// Don't dork with fonts for zero-width characters and the embedded
// object character.
if ((c == 0x200B) || (c == 0x200C) || (c == 0x200D) || (c == 0xFEFF) || (c == Pkg.EMBED_OBJ_CHAR)) {
return 1;
}
if (c == '\t') {
c = ' ';
}
ValidationData validation = new ValidationData();
int nSplit = 1;
//int nRemove = 0;
TextAttr poAttr = moRun.r().getAttr();
boolean bInvisible = false;
if ((poAttr != null) && poAttr.invisibleEnable() && poAttr.invisible()) {
bInvisible = true;
}
// If this is a single base character (most common case) or is invisible,
// don't invoke the overhead of looking for precomposed forms; simply
// validate the character given.
if ((mnAccentRunSize == 1) || bInvisible) {
validateBaseChar (c, validation);
if (validation.fontInstance == null) {
return 1;
}
}
// A real accent run: WRServices may combine the base character plus some
// number of accents into a precomposed Unicode form before looking up
// glyph ID. In order to get the correct font pre-validation, try to
// find a precomposed form in the font. Start with the longest possible
// base+accent(s) sequence and then pop accents off the end until a match
// is found, indicated by variable nSplit.
else {
Composition oComposition = new Composition (moAccentRun);
oComposition.reconcile();
for (; ; ) {
c = oComposition.getPrecomposedChar();
validateBaseChar (c, validation);
if (validation.fontInstance == null) {
break;
}
if (oComposition.getSplit() == 1) {
return 1; // can't even validate base char
}
oComposition.popCombiningMark(); // retry with shorter run
}
nSplit = oComposition.getSplit();
if (oComposition.doComposition()) {
assert (nSplit > 1);
nComposed = nSplit;
for (int i = 1; i < nSplit; i++) {
moAccentRun[i] = moAccentRun[nSplit+i-1];
}
mnAccentRunSize -= nSplit - 1;
moAccentRun[0] = c;
meAccentRunBreak = TextCharProp.getCharProperty (c);
nSplit = 1; // in case any unresolved accents after
}
}
// Run through any remaining accents, trying to validate them in the
// current font. If one is missing, try to find a font that represents it
// _and_ has all the (base+accent) characters tested so far.
for (int nAccentIndex = nSplit; nAccentIndex < mnAccentRunSize; nAccentIndex++) {
int cAccent = substituteInvisibleCharacter (moAccentRun[nAccentIndex]);
moAccentRun[nAccentIndex] = cAccent;
if ((cAccent != '\0') && (! validateChar (validation.fontInstance, cAccent))) {
FontInstance oTestInstance = reconcileFont (validation.fontInstance, cAccent);
if ((oTestInstance != null) && (oTestInstance != validation.fontInstance)) {
int i = 0;
if (validateChar (oTestInstance, c)) {
for (i = nSplit; i < nAccentIndex; i++) {
if (! validateChar (oTestInstance, moAccentRun[i])) {
break;
}
}
}
if (i >= nAccentIndex) {
validation.fontInstance = oTestInstance;
validation.replaceFont = true;
}
}
}
}
// If there is a font change required (either for the base or at least
// one of the accent characters), create a new run with this font.
if (validation.replaceFont) {
TextAttr poOverrideAttr = new TextAttr (validation.originalAttr);
boolean bSubstitute = false;
FontItem poFontItem = validation.fontInstance.getFontItem();
FontItem poOriginalItem = validation.originalFontInstance.getFontItem();
if ( /* (poFontItem.getEncoding() != poOriginalItem.getEncoding())
|| */ (! poFontItem.equals (poOriginalItem))) {
bSubstitute = true;
}
poOverrideAttr.fontInstance (validation.fontInstance, poOriginalItem.getTypeface());
if (bSubstitute) {
mpoLine.display().setFontSubstitution (true);
}
// Grab ref before changing run, because poOriginalAttr may be temporary.
mpoPendingAttr = validation.originalAttr;
DispRun oNewRun = new DispRun (moRun.r(), poOverrideAttr);
moRun.reset (oNewRun);
}
return nComposed;
}
private void validateBaseChar (int c, ValidationData validation) {
boolean bValidated = false;
// If there is no override in effect, there is only one attribute in
// which to validate the character. Otherwise, there are two choices:
// the override (in moRun.gertAttr()), and the original (mpoPenfingAttr).
// In this case, try the original first, as we want to get back to the
// requested font if possible. If that fails, we'll then try the
// override before looking for yet another font to use. // no override in effect ...
if (mpoPendingAttr == null) {
validation.originalAttr = moRun.r().getAttr();
} else {
validation.originalAttr = mpoPendingAttr;
if (validateChar (mpoPendingAttr.fontInstance(), c)) {
DispRun oNewRun = new DispRun (moRun.r(), mpoPendingAttr);
moRun.reset (oNewRun);
mpoPendingAttr = null;
bValidated = true;
}
}
// Not validated at this point if there is no override, or there is an
// override and the character didn't validate in the original font. Here
// we try to validate in the "current" font (original if no override, or
// override if one present).
if (! bValidated) {
TextAttr poAttr = moRun.r().getAttr();
if (poAttr == null) {
poAttr = validation.originalAttr;
}
if (poAttr != null) {
bValidated = validateChar (poAttr.fontInstance(), c);
}
}
// Establish the initial font instance. If there's no need for a run
// change, this is simply the current run's font. Otherwise, try to find
// a font that has the base character.
if (bValidated) {
TextAttr poAttr = moRun.r().getAttr();
if (poAttr != null) {
validation.originalFontInstance = poAttr.fontInstance();
validation.fontInstance = validation.originalFontInstance;
}
} else {
assert (validation.originalAttr != null);
validation.originalFontInstance = validation.originalAttr.fontInstance();
validation.fontInstance = reconcileFont (validation.originalFontInstance, c);
validation.replaceFont = true;
}
}
private boolean validateChar (FontInstance oFontInstance, int c) {
if (oFontInstance != null) {
// try {
FontItem poFontItem = oFontInstance.getFontItem();
if ((poFontItem != null) && (poFontItem.validateChar (c, GFXGlyphOrientation.usesHorizontalGlyphs (meGlyphOrientation)))) {
return true; // TODO:
}
// }
// catch (11) {
// }
}
//
return false;
}
private FontInstance reconcileFont (FontInstance oFontInstance, int c) {
TextAttr poAttr = moRun.r().getAttr();
if (poAttr == null) {
return null;
}
// try {
return poAttr.gfxSource().reconcileFont (oFontInstance, c);
// }
// catch (11) {
// }
// return FontInstance();
}
private int substituteInvisibleCharacter (int c) {
TextAttr poAttr = moRun.r().getAttr();
if (poAttr != null) {
if (poAttr.invisibleEnable() && poAttr.invisible()) {
if (poAttr.invisCharEnable() && (poAttr.invisChar() != '\0')) {
c = poAttr.invisChar();
}
}
}
return c;
}
private void addChar (int c, int eBreak) {
mpoLine.addChar (c, eBreak);
}
private static boolean charIsRomanRotateable (int character) {
// CharRangeCode eCharRange = CharConverter.getCharRange (character);
// return CharConverter.isCharRangeLatin (eCharRange) || ((character >= 0xFB00) && (character < 0xFB07)) || ((character >= 0xFF61) && (character <= 0xFF9F));
return ((character >= 0x41) && (character < 0x5A))
|| ((character >= 0x61) && (character < 0x7A))
|| ((character >= 0xFB00) && (character < 0xFB07))
|| ((character >= 0xFF61) && (character <= 0xFF9F));
}
private static int figureVerticalOrientationsFromChar (int c) {
// we don't want ATE::kBaselineRotatedRomanInVertical for space characters and maybe others.
return charIsRomanRotateable (c) ? GFXGlyphOrientation.HORIZONTAL_ROTATED : GFXGlyphOrientation.VERTICAL;
}
}
//----------------------------------------------------------------------
//
// DispRaw - Base class
//
//----------------------------------------------------------------------
abstract class DispRaw {
private final PosnStack moPosnStack;
private TextPosnBase mpoCurrent;
private TextStream mpoStopStream;
private int mnStopIndex;
private boolean mbContinue;
private boolean mbIsLastParaLine;
private boolean mbIsLastLineInStream;
DispRaw (FormatInfo oFormatInfo, PosnStack oPosnStack) {
moPosnStack = oPosnStack;
mpoCurrent = moPosnStack.top();
mpoCurrent.position (TextPosn.POSN_BEFORE);
if (oFormatInfo.getChange().type() == DispChange.CHANGE_FRAME) {
mpoStopStream = oFormatInfo.getChange().stream();
mnStopIndex = oFormatInfo.getChange().index() + oFormatInfo.getChange().count();
}
}
TextPosnBase getPosition () {
return mpoCurrent;
}
boolean canContinue () {
return mbContinue;
}
void run () {
//int nPushed = 0;
boolean bDone = false;
while (! bDone) {
if (mpoStopStream != null) {
if ((mpoCurrent.index() >= mnStopIndex) && (mpoCurrent.stream() == mpoStopStream)) {
return;
}
}
int eItem = mpoCurrent.next (true);
boolean bAdvanced = false;
switch (eItem) {
case TextItem.ATTR:
bAdvanced = onAttr();
break;
case TextItem.CHAR:
int c = mpoCurrent.nextChar (true);
int eData = TextCharProp.getCharProperty (c);
int eBreak = TextCharProp.getBreakClass (eData);
if ((eBreak == TextCharProp.BREAK_BK)
|| (eBreak == TextCharProp.BREAK_CR)
|| (eBreak == TextCharProp.BREAK_LF)
|| (eBreak == TextCharProp.BREAK_NL)) {
bDone = true;
mbContinue = true;
c = ' ';
eData = TextCharProp.makeData (TextCharProp.BREAK_SP,
TextCharProp.getWidthClass (eData),
TextCharProp.GRAPHEME_Default,
TextCharProp.WORD_Default,
TextCharProp.CASE_Default,
TextCharProp.BIDI_WS,
TextCharProp.BM_OFF);
}
bAdvanced = onChar (c, eData);
break;
case TextItem.FIELD:
TextField poField = mpoCurrent.nextField();
TextPosnBase oPosn = new TextPosnBase (poField, TextPosn.POSN_BEFORE);
moPosnStack.push (oPosn);
mpoCurrent = moPosnStack.top();
onStreamStart();
bAdvanced = true; // don't skip first item in stream
break;
case TextItem.OBJECT:
bAdvanced = onObject();
break;
case TextItem.PARA:
bAdvanced = onPara();
mbIsLastParaLine = true;
bDone = true;
mbContinue = true;
break;
case TextItem.UNKNOWN: // end of stream
assert (moPosnStack.size() > 0);
moPosnStack.pop();
if (moPosnStack.size() == 0) {
mbIsLastParaLine = true;
mbIsLastLineInStream = true;
bDone = true;
} else {
mpoCurrent = moPosnStack.top();
bAdvanced = true; // skipped over field ref when we found the field
onStreamEnd();
}
break;
}
if (! bAdvanced) {
mpoCurrent.next();
}
}
}
boolean isLastParaLine () {
return mbIsLastParaLine;
}
boolean isLastLineInStream () {
return mbIsLastLineInStream;
}
protected boolean onAttr () {
return false;
}
abstract protected boolean onChar (int c, int eData);
abstract protected boolean onObject ();
abstract protected boolean onPara ();
protected void onStreamStart () {
}
protected void onStreamEnd () {
}
}
//----------------------------------------------------------------------
//
// DispRawCounter - Count the number of characters to
// allocate
//
//----------------------------------------------------------------------
class RawCounter extends DispRaw {
private int mnCount;
RawCounter (FormatInfo oFormatInfo, PosnStack oPosnStack) {
super (oFormatInfo, oPosnStack);
mnCount = 0;
}
int getCount () {
return mnCount;
}
protected boolean onChar (int c, int eData) {
mnCount++;
return false;
}
protected boolean onObject () {
mnCount++;
return false;
}
protected boolean onPara () {
mnCount++;
return false;
}
}
//----------------------------------------------------------------------
//
// DispRawBuilder - Build the raw line
//
//----------------------------------------------------------------------
class RawBuilder extends DispRaw {
private final RawProcessor moProcessor;
RawBuilder (FormatInfo oFormatInfo, DispLineRaw poLine) {
super (oFormatInfo, oFormatInfo.posnStack());
moProcessor = new RawProcessor (oFormatInfo, poLine, getPosition(), oFormatInfo.resolveAttr (oFormatInfo.getAttr()));
}
void finish () {
moProcessor.finish (isLastParaLine(), isLastLineInStream());
}
protected boolean onAttr () {
moProcessor.processAttr (getPosition().nextAttr());
return true;
}
protected boolean onChar (int c, int eBreak) {
moProcessor.processChar (c, eBreak, getPosition());
return false;
}
protected boolean onObject () {
moProcessor.processObject (getPosition().nextEmbed(), getPosition());
return true;
}
protected boolean onPara () {
moProcessor.processPara (getPosition());
return false;
}
protected void onStreamStart () {
moProcessor.processStreamStart (getPosition());
}
protected void onStreamEnd () {
moProcessor.processStreamEnd (getPosition());
}
}