Well, I played around with the API and it wasn't too difficult. Here's the code to insert all of the state numbers into a copy of a grammar file either before or after the region of the grammar that has been recognized when the state is entered. Honestly I'm not sure what it means when an interval is null. This seems to be the case for approximately a third of the states.
The code for inserting into a file is taken verbatim from xor_eq's answer.
The result of this code looks like this:

private static String GRAMMAR_FILE_NAME = "JavaSimple.g4";
private static String EDITED_GRAMMAR_FILE_NAME = "JavaSimple_edited.g4";
private static void insertStateNumbersIntoGrammar() throws IOException, RecognitionException {
copyGrammarFile();
// Load tokens
ANTLRInputStream input = new ANTLRFileStream(GRAMMAR_FILE_NAME);
ANTLRv4Lexer lexer = new ANTLRv4Lexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
tokens.fill();
// Load Grammar
String contents = new String(Files.readAllBytes(Paths.get(GRAMMAR_FILE_NAME)));
Grammar g = new Grammar(contents);
List<Insert> inserts = new ArrayList<Insert>();
boolean before = false;
for (ATNState state : g.atn.states) {
int stateNr = state.stateNumber;
Interval interval = g.getStateToGrammarRegion(stateNr);
if (interval != null) {
Token token = before ? tokens.get(interval.a) : tokens.get(interval.b);
int i = before ? token.getStartIndex() : token.getStopIndex() + 1;
String stateStr = "[" + stateNr + "]";
long insertSize = calcInsertLengthBefore(inserts, i);
insert(EDITED_GRAMMAR_FILE_NAME, i + insertSize, stateStr.getBytes());
inserts.add(new Insert(i, stateStr));
}
}
}
private static int calcInsertLengthBefore(List<Insert> inserts, int index) {
return inserts.stream()
.filter(insert -> insert.index < index)
.flatMapToInt(insert -> IntStream.of(insert.state.length()))
.sum();
}
private static void insert(String filename, long offset, byte[] content) throws IOException {
RandomAccessFile r = new RandomAccessFile(new File(filename), "rw");
RandomAccessFile rtemp = new RandomAccessFile(new File(filename + "~"), "rw");
long fileSize = r.length();
FileChannel sourceChannel = r.getChannel();
FileChannel targetChannel = rtemp.getChannel();
sourceChannel.transferTo(offset, (fileSize - offset), targetChannel);
sourceChannel.truncate(offset);
r.seek(offset);
r.write(content);
long newOffset = r.getFilePointer();
targetChannel.position(0L);
sourceChannel.transferFrom(targetChannel, newOffset, (fileSize - offset));
sourceChannel.close();
targetChannel.close();
}
private static void copyGrammarFile() {
File source = new File(GRAMMAR_FILE_NAME);
File target = new File(EDITED_GRAMMAR_FILE_NAME);
try {
Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
}
private static class Insert {
final Integer index;
final String state;
Insert(int index, String state) {
this.index = index;
this.state = state;
}
}