protected Map<Integer, RewriteOperation> reduceToSingleOperationPerIndex(List<RewriteOperation> rewrites) {
//		System.out.println("rewrites="+rewrites);

		// WALK REPLACES
		for (int i = 0; i < rewrites.size(); i++) {
			RewriteOperation op = rewrites.get(i);
			if ( op==null ) continue;
			if ( !(op instanceof ReplaceOp) ) continue;
			ReplaceOp rop = (ReplaceOp)rewrites.get(i);
			// Wipe prior inserts within range
			List<? extends InsertBeforeOp> inserts = getKindOfOps(rewrites, InsertBeforeOp.class, i);
			for (InsertBeforeOp iop : inserts) {
				if ( iop.index == rop.index ) {
					// E.g., insert before 2, delete 2..2; update replace
					// text to include insert before, kill insert
					rewrites.set(iop.instructionIndex, null);
					rop.text = iop.text.toString() + (rop.text!=null?rop.text.toString():"");
				}
				else if ( iop.index > rop.index && iop.index <= rop.lastIndex ) {
					// delete insert as it's a no-op.
					rewrites.set(iop.instructionIndex, null);
				}
			}
			// Drop any prior replaces contained within
			List<? extends ReplaceOp> prevReplaces = getKindOfOps(rewrites, ReplaceOp.class, i);
			for (ReplaceOp prevRop : prevReplaces) {
				if ( prevRop.index>=rop.index && prevRop.lastIndex <= rop.lastIndex ) {
					// delete replace as it's a no-op.
					rewrites.set(prevRop.instructionIndex, null);
					continue;
				}
				// throw exception unless disjoint or identical
				boolean disjoint =
					prevRop.lastIndex<rop.index || prevRop.index > rop.lastIndex;
				// Delete special case of replace (text==null):
				// D.i-j.u D.x-y.v	| boundaries overlap	combine to max(min)..max(right)
				if ( prevRop.text==null && rop.text==null && !disjoint ) {
					//System.out.println("overlapping deletes: "+prevRop+", "+rop);
					rewrites.set(prevRop.instructionIndex, null); // kill first delete
					rop.index = Math.min(prevRop.index, rop.index);
					rop.lastIndex = Math.max(prevRop.lastIndex, rop.lastIndex);
					System.out.println("new rop "+rop);
				}
				else if ( !disjoint ) {
					throw new IllegalArgumentException("replace op boundaries of "+rop+" overlap with previous "+prevRop);
				}
			}
		}

		// WALK INSERTS
		for (int i = 0; i < rewrites.size(); i++) {
			RewriteOperation op = rewrites.get(i);
			if ( op==null ) continue;
			if ( !(op instanceof InsertBeforeOp) ) continue;
			InsertBeforeOp iop = (InsertBeforeOp)rewrites.get(i);
			// combine current insert with prior if any at same index
			List<? extends InsertBeforeOp> prevInserts = getKindOfOps(rewrites, InsertBeforeOp.class, i);
			for (InsertBeforeOp prevIop : prevInserts) {
				if ( prevIop.index==iop.index ) {
					if ( InsertAfterOp.class.isInstance(prevIop) ) {
						iop.text = catOpText(prevIop.text, iop.text);
						rewrites.set(prevIop.instructionIndex, null);
					}
					else if ( InsertBeforeOp.class.isInstance(prevIop) ) { // combine objects
						// convert to strings...we're in process of toString'ing
						// whole token buffer so no lazy eval issue with any templates
						iop.text = catOpText(iop.text, prevIop.text);
						// delete redundant prior insert
						rewrites.set(prevIop.instructionIndex, null);
					}
				}
			}
			// look for replaces where iop.index is in range; error
			List<? extends ReplaceOp> prevReplaces = getKindOfOps(rewrites, ReplaceOp.class, i);
			for (ReplaceOp rop : prevReplaces) {
				if ( iop.index == rop.index ) {
					rop.text = catOpText(iop.text,rop.text);
					rewrites.set(i, null);	// delete current insert
					continue;
				}
				if ( iop.index >= rop.index && iop.index <= rop.lastIndex ) {
					throw new IllegalArgumentException("insert op "+iop+" within boundaries of previous "+rop);
				}
			}
		}
		// System.out.println("rewrites after="+rewrites);
		Map<Integer, RewriteOperation> m = new HashMap<Integer, RewriteOperation>();
		for (int i = 0; i < rewrites.size(); i++) {
			RewriteOperation op = rewrites.get(i);
			if ( op==null ) continue; // ignore deleted ops
			if ( m.get(op.index)!=null ) {
				throw new Error("should only be one op per index");
			}
			m.put(op.index, op);
		}
		//System.out.println("index to op: "+m);
		return m;
	}
We need to combine operations and report invalid operations (like
 overlapping replaces that are not completed nested). Inserts to
 same index need to be combined etc...  Here are the cases:

 I.i.u I.j.v								leave alone, nonoverlapping
 I.i.u I.i.v								combine: Iivu

 R.i-j.u R.x-y.v	| i-j in x-y			delete first R
 R.i-j.u R.i-j.v							delete first R
 R.i-j.u R.x-y.v	| x-y in i-j			ERROR
 R.i-j.u R.x-y.v	| boundaries overlap	ERROR

 Delete special case of replace (text==null):
 D.i-j.u D.x-y.v	| boundaries overlap	combine to max(min)..max(right)

 I.i.u R.x-y.v | i in (x+1)-y			delete I (since insert before
											we're not deleting i)
 I.i.u R.x-y.v | i not in (x+1)-y		leave alone, nonoverlapping
 R.x-y.v I.i.u | i in x-y				ERROR
 R.x-y.v I.x.u 							R.x-y.uv (combine, delete I)
 R.x-y.v I.i.u | i not in x-y			leave alone, nonoverlapping

 I.i.u = insert u before op @ index i
 R.x-y.u = replace x-y indexed tokens with u

 First we need to examine replaces. For any replace op:

		1. wipe out any insertions before op within that range.
		2. Drop any replace op before that is contained completely within
	 that range.
		3. Throw exception upon boundary overlap with any previous replace.

 Then we can deal with inserts:

		1. for any inserts to same index, combine even if not adjacent.
		2. for any prior replace with same left boundary, combine this
	 insert with replace and delete this replace.
		3. throw exception if index in same range as previous replace

 Don't actually delete; make op null in list. Easier to walk list.
 Later we can throw as we add to index &rarr; op map.

 Note that I.2 R.2-2 will wipe out I.2 even though, technically, the
 inserted stuff would be before the replace range. But, if you
 add tokens in front of a method body '{' and then delete the method
 body, I think the stuff before the '{' you added should disappear too.

 Return a map from token index to operation.
runtime/Java/src/org/antlr/v4/runtime/TokenStreamRewriter.java