* @param msg the annotation message
* @param formatArgs format arguments to pass to String.format
*/
public void annotate(int length, @Nonnull String msg, Object... formatArgs) {
if (startLimit != -1 && endLimit != -1 && (cursor < startLimit || cursor >= endLimit)) {
throw new ExceptionWithContext("Annotating outside the parent bounds");
}
String formattedMsg;
if (formatArgs != null && formatArgs.length > 0) {
formattedMsg = String.format(msg, formatArgs);
} else {
formattedMsg = msg;
}
int exclusiveEndOffset = cursor + length;
AnnotationEndpoint endPoint = null;
// Do we have an endpoint at the beginning of this annotation already?
AnnotationEndpoint startPoint = annotatations.get(cursor);
if (startPoint == null) {
// Nope. We need to check that we're not in the middle of an existing range annotation.
Map.Entry<Integer, AnnotationEndpoint> previousEntry = annotatations.lowerEntry(cursor);
if (previousEntry != null) {
AnnotationEndpoint previousAnnotations = previousEntry.getValue();
AnnotationItem previousRangeAnnotation = previousAnnotations.rangeAnnotation;
if (previousRangeAnnotation != null) {
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(previousEntry.getKey(),
previousRangeAnnotation.annotation));
}
}
} else if (length > 0) {
AnnotationItem existingRangeAnnotation = startPoint.rangeAnnotation;
if (existingRangeAnnotation != null) {
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(cursor, existingRangeAnnotation.annotation));
}
}
if (length > 0) {
// Ensure that there is no later annotation that would intersect with this one
Map.Entry<Integer, AnnotationEndpoint> nextEntry = annotatations.higherEntry(cursor);
if (nextEntry != null) {
int nextKey = nextEntry.getKey();
if (nextKey < exclusiveEndOffset) {
// there is an endpoint that would intersect with this annotation. Find one of the annotations
// associated with the endpoint, to print in the error message
AnnotationEndpoint nextEndpoint = nextEntry.getValue();
AnnotationItem nextRangeAnnotation = nextEndpoint.rangeAnnotation;
if (nextRangeAnnotation != null) {
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(nextKey, nextRangeAnnotation.annotation));
}
if (nextEndpoint.pointAnnotations.size() > 0) {
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(nextKey, nextKey,
nextEndpoint.pointAnnotations.get(0).annotation));
}
// There are no annotations on this endpoint. This "shouldn't" happen. We can still throw an exception.
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation endpoint at %d",
formatAnnotation(cursor, cursor + length, formattedMsg),
nextKey);
}