Java tutorial
/******************************************************************************* * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.xtext.generator.trace; import static com.google.common.collect.Lists.*; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import com.google.common.collect.Lists; /** * Copied and adapted from * <code>org.eclipse.xtext.ui.editor.syntaxcoloring.MergingHighlightedPositionAcceptor</code>. * * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. * @author Jan Koehnlein - Initial contribution and API */ public class TraceRegionMerger { private List<AbstractTraceRegion> leafRegions; public AbstractTraceRegion mergeTraceRegions(List<AbstractTraceRegion> roots) { leafRegions = newArrayList(); for (AbstractTraceRegion root : roots) { for (Iterator<AbstractTraceRegion> iter = root.leafIterator(); iter.hasNext();) leafRegions.add(clone(iter.next())); } mergeLeafRegions(); AbstractTraceRegion firstLeaf = leafRegions.get(0); AbstractTraceRegion lastLeaf = leafRegions.get(leafRegions.size() - 1); AbstractTraceRegion mergedRoot = new TraceRegion(firstLeaf.getMyOffset(), lastLeaf.getMyOffset() + lastLeaf.getMyLength() - firstLeaf.getMyOffset(), firstLeaf.getMyLineNumber(), lastLeaf.getMyEndLineNumber(), true, Collections.<ILocationData>emptyList(), null); for (AbstractTraceRegion leafRegion : leafRegions) leafRegion.setParent(mergedRoot); return mergedRoot; } private void mergeLeafRegions() { if (leafRegions.size() < 2) return; Collections.sort(leafRegions, getComparator()); AbstractTraceRegion prev = leafRegions.get(0); int i = 1; while (i < leafRegions.size()) { AbstractTraceRegion next = leafRegions.get(i); int exclusiveEndOffset = prev.getMyOffset() + prev.getMyLength(); int exclusiveEndLine = prev.getMyEndLineNumber(); if (next.getMyOffset() < exclusiveEndOffset) { int newLength = next.getMyOffset() - prev.getMyOffset(); prev = replaceTruncated(i - 1, prev, newLength, next.getMyLineNumber()); doMergeLeafRegions(i, exclusiveEndOffset, exclusiveEndLine, prev.getAssociatedLocations()); if (prev.getMyLength() == 0) { if (prev != leafRegions.remove(i - 1)) throw new IllegalStateException("removed position is not 'prev'"); } } if (prev.getMyLength() != 0) { i++; } prev = leafRegions.get(i - 1); } } private void doMergeLeafRegions(int listIdx, int exclusiveEndOffset, int inclusiveEndLine, List<ILocationData> locations) { int i = listIdx; List<AbstractTraceRegion> newRegions = null; AbstractTraceRegion prev = null; while (i < leafRegions.size()) { AbstractTraceRegion next = leafRegions.get(i); if (next.getMyOffset() >= exclusiveEndOffset) { newRegions = addPendingRegions(prev, exclusiveEndOffset, inclusiveEndLine, locations, newRegions); partialSortRegions(listIdx, exclusiveEndOffset, i, newRegions); return; } if (prev != null) { int prevEnd = prev.getMyOffset() + prev.getMyLength(); if (prevEnd < next.getMyOffset()) { if (newRegions == null) newRegions = Lists.newArrayListWithExpectedSize(4); newRegions.add(new TraceRegion(prevEnd, next.getMyOffset() - prevEnd, prev.getMyEndLineNumber(), next.getMyLineNumber(), true, locations, null)); } } if (next.getMyOffset() + next.getMyLength() <= exclusiveEndOffset) { next = replaceMerged(i, next, locations); } else { int oldLength = next.getMyLength(); int oldEndLine = next.getMyEndLineNumber(); next = replaceTruncated(i, next, exclusiveEndOffset - next.getMyOffset(), inclusiveEndLine); if (newRegions == null) newRegions = Lists.newArrayListWithExpectedSize(4); newRegions.add( new TraceRegion(next.getMyOffset() + next.getMyLength(), oldLength - next.getMyLength(), inclusiveEndLine, oldEndLine, true, next.getAssociatedLocations(), null)); next = replaceMerged(i, next, locations); } i++; prev = next; } newRegions = addPendingRegions(prev, exclusiveEndOffset, inclusiveEndLine, locations, newRegions); partialSortRegions(listIdx, exclusiveEndOffset, i, newRegions); } private void partialSortRegions(int listIdx, int exclusiveEndOffset, int insertionIndex, List<AbstractTraceRegion> addedRegions) { int addedRegionsSize = addedRegions != null ? addedRegions.size() : 0; if (addedRegionsSize != 0) leafRegions.addAll(insertionIndex, addedRegions); if (insertionIndex + addedRegionsSize != listIdx) { int endIdx = insertionIndex + addedRegionsSize; while (endIdx < leafRegions.size() && leafRegions.get(endIdx).getMyOffset() == exclusiveEndOffset) { endIdx++; } Collections.sort(leafRegions.subList(listIdx, endIdx), getComparator()); } } private List<AbstractTraceRegion> addPendingRegions(AbstractTraceRegion pending, int expectedEndOffset, int expectedEndLine, List<ILocationData> locations, List<AbstractTraceRegion> result) { if (pending != null) { int prevEnd = pending.getMyOffset() + pending.getMyLength(); if (prevEnd < expectedEndOffset) { AbstractTraceRegion fillRegion = new TraceRegion(prevEnd, expectedEndOffset - prevEnd, pending.getMyEndLineNumber(), expectedEndLine, true, locations, null); if (result == null) result = Collections.singletonList(fillRegion); else result.add(fillRegion); } } return result; } private Comparator<AbstractTraceRegion> getComparator() { return new Comparator<AbstractTraceRegion>() { @Override public int compare(AbstractTraceRegion left, AbstractTraceRegion right) { int offsetDelta = left.getMyOffset() - right.getMyOffset(); return (offsetDelta != 0) ? offsetDelta : right.getMyLength() - left.getMyLength(); } }; } private AbstractTraceRegion replaceTruncated(int index, AbstractTraceRegion region, int newLength, int newEndLine) { AbstractTraceRegion truncated = new TraceRegion(region.getMyOffset(), newLength, region.getMyLineNumber(), newEndLine, true, region.getAssociatedLocations(), null); leafRegions.set(index, truncated); return truncated; } private AbstractTraceRegion replaceMerged(int index, AbstractTraceRegion region, List<ILocationData> locationData) { List<ILocationData> mergedLocations = newArrayList(region.getAssociatedLocations()); mergedLocations.addAll(locationData); AbstractTraceRegion merged = new TraceRegion(region.getMyOffset(), region.getMyLength(), region.getMyLineNumber(), region.getMyEndLineNumber(), true, mergedLocations, null); leafRegions.set(index, merged); return merged; } private AbstractTraceRegion clone(AbstractTraceRegion region) { AbstractTraceRegion merged = new TraceRegion(region.getMyOffset(), region.getMyLength(), region.getMyLineNumber(), region.getMyEndLineNumber(), true, region.getAssociatedLocations(), null); return merged; } }