addPosition -= removeSize; // new insert should shift down
}
int remainder = doc.size() - removeSize - addPosition; // remainder after the insert
// step 1: initialise the operations for the DOM and annotations:
DocOpBuffer domOp = new DocOpBuffer();
final AnnotationsNormalizer<DocOp> annotOp =
new AnnotationsNormalizer<DocOp>(new UncheckedDocOpBuffer());
if (addPosition > 0) {
annotOp.retain(addPosition);
domOp.retain(addPosition);
}
// step 2: fill in the DOM operations:
for (N at = from; at != toExcl; at = doc.getNextSibling(at)) {
DomOperationUtil.buildDomInitializationFromSubtree(doc, at, domOp);
}
// step 3: form the annotatinos
AnnotationInterval<String> last = null;
doc.knownKeys().each(new Proc() { // start all the keys
@Override public void apply(String key) {
annotOp.startAnnotation(key, null, doc.getAnnotation(removeStart, key));
}
});
for (AnnotationInterval<String> interval : // update along the way
doc.annotationIntervals(removeStart, removeEnd, null)) {
interval.diffFromLeft().each(new ProcV<Object>() {
@Override public void apply(String key, Object value) {
assert value == null || value instanceof String;
if (value != null) {
annotOp.startAnnotation(key, null, (String) value);
} else {
annotOp.endAnnotation(key);
}
}
});
annotOp.retain(interval.length());
last = interval;
}
doc.knownKeys().each(new Proc() { // close all the keys
@Override public void apply(String key) {
annotOp.endAnnotation(key);
}
});
// step 4: close them out:
if (remainder > 0) {
domOp.retain(remainder);
annotOp.retain(remainder);
}
// step 5: remove the entire range
deleteRange(removeStart, removeEnd);
// step 6: compose then consume the insertion op:
DocOp atomicInsert;
try {
atomicInsert = Composer.compose(domOp.finish(), annotOp.finish());
hackConsume(Nindo.fromDocOp(atomicInsert, true));
} catch (OperationException e) {
// should never happen, we constructed composable ops
}
}