IndexedRun.java
package pro.verron.officestamper.core;
import org.docx4j.wml.R;
import org.docx4j.wml.RPr;
/**
* Represents a run (i.e., a text fragment) in a paragraph. The run is indexed relative to the containing paragraph
* and also relative to the containing document.
*
* @param startIndex the start index of the run relative to the containing paragraph.
* @param endIndex the end index of the run relative to the containing paragraph.
* @param indexInParent the index of the run relative to the containing document.
* @param run the run itself.
*
* @author Joseph Verron
* @author Tom Hombergs
* @version ${version}
* @since 1.0.0
*/
public record IndexedRun(int startIndex, int endIndex, int indexInParent, R run) {
public int length() {
return getText().length();
}
public String getText() {
return RunUtil.getText(run());
}
public String substring(int endIndex) {
return getText().substring(endIndex);
}
public String substring(int beginIndex, int endIndex) {
return getText().substring(beginIndex, endIndex);
}
public int indexOf(String full) {
return getText().indexOf(full);
}
public RPr getPr() {
return run.getRPr();
}
/**
* Determines whether the specified range of start and end index touches this run.
* <p>
* Example:
* <p>
* Given this run: [a,b,c,d,e,f,g,h,i,j]
* <p>
* And the range [2,5]
* <p>
* This method will return true, because the range touches the run at the indices 2, 3, 4 and 5.
*
* @param globalStartIndex the global index (meaning the index relative to multiple aggregated runs) at which to
* start the range.
* @param globalEndIndex the global index (meaning the index relative to multiple aggregated runs) at which to end
* the range.
*
* @return true, if the range touches this run, false otherwise.
*/
public boolean isTouchedByRange(int globalStartIndex, int globalEndIndex) {
var startBetweenIndices = (globalStartIndex < startIndex) && (startIndex <= globalEndIndex);
var endBetweenIndices = (globalStartIndex < endIndex) && (endIndex <= globalEndIndex);
return startBetweenIndices
|| endBetweenIndices
|| ((startIndex <= globalStartIndex)
&& (globalEndIndex <= endIndex));
}
/**
* Replaces the substring starting at the given index with the given replacement string.
*
* @param globalStartIndex the global index at which to
* start the replacement.
* @param globalEndIndex the global index at which to end
* the replacement.
* @param replacement the string to replace the substring at the specified global index.
*/
public void replace(int globalStartIndex, int globalEndIndex, String replacement) {
int localStartIndex = globalIndexToLocalIndex(globalStartIndex);
int localEndIndex = globalIndexToLocalIndex(globalEndIndex);
var text = RunUtil.getSubstring(run, 0, localStartIndex);
text += replacement;
String runText = RunUtil.getText(run);
if (!runText.isEmpty()) {
text += RunUtil.getSubstring(run, localEndIndex);
}
RunUtil.setText(run, text);
}
/**
* Converts a global index to a local index within the context of this run.
* (meaning the index relative to multiple aggregated runs)
*
* @param globalIndex the global index to convert.
*
* @return the local index corresponding to the given global index.
*/
private int globalIndexToLocalIndex(int globalIndex) {
if (globalIndex < startIndex) return 0;
else if (globalIndex > endIndex) return RunUtil.getLength(run);
else return globalIndex - startIndex;
}
}