// append the string value of the node to the buffer
str.append(text.getData());
}
// Second step: output the text
ValueSequence result = new ValueSequence();
final DocumentBuilderReceiver receiver = new DocumentBuilderReceiver(builder);
int nodeNr;
int currentWidth = 0;
if (offsets == null) {
// no matches: just output the entire text
if (width > str.length())
{width = str.length();}
nodeNr = builder.characters(str.substring(0, width));
result.add(builder.getDocument().getNode(nodeNr));
currentWidth += width;
} else {
// sort the offsets
FastQSort.sort(offsets, 0, offsets.size() - 1);
int nextOffset = 0;
int pos = 0;
int lastNodeNr = -1;
// prepare array for callback function arguments
final Sequence params[] = new Sequence[callback.getSignature().getArgumentCount()];
params[1] = firstProxy;
params[2] = extraArgs;
// handle the first match: if the text to the left of the match
// is larger than half of the width, truncate it.
if (str.length() > width) {
final Match.Offset firstMatch = offsets.get(nextOffset++);
if (firstMatch.getOffset() > 0) {
int leftWidth = (width - firstMatch.getLength()) / 2;
if (firstMatch.getOffset() > leftWidth) {
pos = truncateStart(str, firstMatch.getOffset() - leftWidth, firstMatch.getOffset());
leftWidth = firstMatch.getOffset() - pos;
} else
{leftWidth = firstMatch.getOffset();}
nodeNr = builder.characters(str.substring(pos, pos + leftWidth));
// adjacent chunks of text will be merged into one text node. we may
// thus get duplicate nodes here. check the nodeNr to avoid adding
// the same node twice.
if (lastNodeNr != nodeNr)
{result.add(builder.getDocument().getNode(nodeNr));}
lastNodeNr = nodeNr;
currentWidth += leftWidth;
pos += leftWidth;
}
// put the matching term into argument 0 of the callback function
params[0] = new StringValue(str.substring(firstMatch.getOffset(), firstMatch.getOffset() + firstMatch.getLength()));
// if the callback function accepts 4 arguments, the last argument should contain additional
// information on the match:
if (callback.getSignature().getArgumentCount() == 4) {
params[3] = new ValueSequence();
params[3].add(new IntegerValue(nextOffset - 1));
params[3].add(new IntegerValue(firstMatch.getOffset()));
params[3].add(new IntegerValue(firstMatch.getLength()));
}
// now execute the callback func.
final Sequence callbackResult = callback.evalFunction(null, null, params);
// iterate through the result of the callback
for (final SequenceIterator iter = callbackResult.iterate(); iter.hasNext(); ) {
final Item next = iter.nextItem();
if (Type.subTypeOf(next.getType(), Type.NODE)) {
nodeNr = builder.getDocument().getLastNode();
try {
next.copyTo(context.getBroker(), receiver);
result.add(builder.getDocument().getNode(++nodeNr));
lastNodeNr = nodeNr;
} catch (final SAXException e) {
throw new XPathException(this, "Internal error while copying nodes: " + e.getMessage(), e);
}
}
}
currentWidth += firstMatch.getLength();
pos += firstMatch.getLength();
} else
{width = str.length();}
// output the rest of the text and matches
Match.Offset offset;
for (int i = nextOffset; i < offsets.size() && currentWidth < width; i++) {
offset = offsets.get(i);
if (offset.getOffset() > pos) {
int len = offset.getOffset() - pos;
if (currentWidth + len > width)
{len = width - currentWidth;}
nodeNr = builder.characters(str.substring(pos, pos + len));
if (lastNodeNr != nodeNr)
{result.add(builder.getDocument().getNode(nodeNr));}
currentWidth += len;
pos += len;
}
if (currentWidth + offset.getLength() < width) {
// put the matching term into argument 0 of the callback function
params[0] = new StringValue(str.substring(offset.getOffset(), offset.getOffset() + offset.getLength()));
// if the callback function accepts 4 arguments, the last argument should contain additional
// information on the match:
if (callback.getSignature().getArgumentCount() == 4) {
params[3] = new ValueSequence();
params[3].add(new IntegerValue(i));
params[3].add(new IntegerValue(offset.getOffset()));
params[3].add(new IntegerValue(offset.getLength()));
}
// execute the callback function
final Sequence callbackResult = callback.evalFunction(null, null, params);
for (final SequenceIterator iter = callbackResult.iterate(); iter.hasNext(); ) {
final Item next = iter.nextItem();
if (Type.subTypeOf(next.getType(), Type.NODE)) {
nodeNr = builder.getDocument().getLastNode();
try {
next.copyTo(context.getBroker(), receiver);
result.add(builder.getDocument().getNode(++nodeNr));
lastNodeNr = nodeNr;
} catch (final SAXException e) {
throw new XPathException(this, "Internal error while copying nodes: " + e.getMessage(), e);
}
}
}
currentWidth += offset.getLength();
pos += offset.getLength();
} else
{break;}
}
// print the final text chunk if more space is available
if (currentWidth < width && pos < str.length()) {
boolean truncated = false;
int len = str.length() - pos;
if (len > width - currentWidth) {
truncated = true;
len = width - currentWidth;
}
nodeNr = builder.characters(str.substring(pos, pos + len));
if (lastNodeNr != nodeNr)
{result.add(builder.getDocument().getNode(nodeNr));}
lastNodeNr = nodeNr;
currentWidth += len;
if (truncated) {
nodeNr = builder.characters(" ...");
if (lastNodeNr != nodeNr)
{result.add(builder.getDocument().getNode(nodeNr));}
lastNodeNr = nodeNr;
}
}
}