| /* |
| * Copyright (c) 2011 Google Inc. |
| * |
| * 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 com.google.eclipse.protobuf.ui.util.editor; |
| |
| import static com.google.common.collect.Lists.newArrayList; |
| import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID; |
| import static com.google.eclipse.protobuf.ui.util.editor.Messages.*; |
| import static org.eclipse.compare.rangedifferencer.RangeDifferencer.findDifferences; |
| import static org.eclipse.core.filebuffers.FileBuffers.createTextFileBufferManager; |
| import static org.eclipse.core.runtime.IStatus.ERROR; |
| import static org.eclipse.core.runtime.Status.OK_STATUS; |
| import static org.eclipse.core.runtime.SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK; |
| |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.apache.log4j.Logger; |
| import org.eclipse.compare.rangedifferencer.RangeDifference; |
| import org.eclipse.core.filebuffers.*; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.jface.text.*; |
| |
| import com.google.inject.Singleton; |
| |
| /** |
| * Utility methods related to editors. Adapted from CDT's |
| * {@code org.eclipse.cdt.internal.ui.util.EditorUtility}. |
| * |
| * @author alruiz@google.com (Alex Ruiz) |
| */ |
| @Singleton public class ChangedLineRegionCalculator { |
| private static Logger logger = Logger.getLogger(ChangedLineRegionCalculator.class); |
| |
| public IRegion[] calculateChangedLineRegions(final ITextFileBuffer buffer, final IDocument current, |
| final IProgressMonitor monitor) throws CoreException { |
| final AtomicReference<IRegion[]> result = new AtomicReference<IRegion[]>(); |
| final AtomicReference<IStatus> errorStatus = new AtomicReference<IStatus>(OK_STATUS); |
| try { |
| SafeRunner.run(new ISafeRunnable() { |
| @Override public void handleException(Throwable exception) { |
| logger.error(exception.getMessage(), exception); |
| errorStatus.set(new Status(ERROR, PLUGIN_ID, 0, errorCalculatingChangedRegions, exception)); |
| result.set(null); |
| } |
| |
| @Override public void run() throws Exception { |
| monitor.beginTask(calculatingChangedRegions, 20); |
| IFileStore fileStore = buffer.getFileStore(); |
| ITextFileBufferManager fileBufferManager = createTextFileBufferManager(); |
| fileBufferManager.connectFileStore(fileStore, getSubProgressMonitor(monitor, 15)); |
| try { |
| IDocument old = ((ITextFileBuffer) fileBufferManager.getFileStoreFileBuffer(fileStore)).getDocument(); |
| result.set(getChangedLineRegions(old)); |
| } finally { |
| fileBufferManager.disconnectFileStore(fileStore, getSubProgressMonitor(monitor, 5)); |
| monitor.done(); |
| } |
| } |
| |
| /* |
| * Returns regions of all lines which differ comparing {@code old}s content with {@code current}s content. |
| * Successive lines are merged into one region. |
| */ |
| private IRegion[] getChangedLineRegions(IDocument old) { |
| RangeDifference[] differences = differencesWith(old); |
| List<IRegion> regions = newArrayList(); |
| int numberOfLines = current.getNumberOfLines(); |
| for (RangeDifference difference : differences) { |
| if (difference.kind() == RangeDifference.CHANGE) { |
| int startLine = Math.min(difference.rightStart(), numberOfLines - 1); |
| int endLine = difference.rightEnd() - 1; |
| IRegion startLineRegion; |
| try { |
| startLineRegion = current.getLineInformation(startLine); |
| if (startLine >= endLine) { |
| // startLine > endLine indicates a deletion of one or more lines. |
| // Deletions are ignored except at the end of the document. |
| if (startLine == endLine |
| || startLineRegion.getOffset() + startLineRegion.getLength() == current.getLength()) { |
| regions.add(startLineRegion); |
| } |
| continue; |
| } |
| IRegion endLineRegion = current.getLineInformation(endLine); |
| int startOffset = startLineRegion.getOffset(); |
| int endOffset = endLineRegion.getOffset() + endLineRegion.getLength(); |
| regions.add(new Region(startOffset, endOffset - startOffset)); |
| } catch (BadLocationException e) { |
| logger.error(e.getMessage(), e); |
| } |
| } |
| } |
| return regions.toArray(new IRegion[regions.size()]); |
| } |
| |
| private RangeDifference[] differencesWith(IDocument old) { |
| return findDifferences(new LineComparator(old), new LineComparator(current)); |
| } |
| }); |
| } finally { |
| IStatus status = errorStatus.get(); |
| if (!status.isOK()) { |
| throw new CoreException(status); |
| } |
| } |
| return result.get(); |
| } |
| |
| private static IProgressMonitor getSubProgressMonitor(IProgressMonitor monitor, int ticks) { |
| if (monitor != null) { |
| return new SubProgressMonitor(monitor, ticks, PREPEND_MAIN_LABEL_TO_SUBTASK); |
| } |
| return new NullProgressMonitor(); |
| } |
| } |