| /******************************************************************************* |
| * Copyright (c) 2006, 2014 Wind River Systems, Inc. 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 |
| * |
| * Contributors: |
| * Markus Schorn - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.core; |
| |
| import java.util.ArrayDeque; |
| import java.util.Deque; |
| import java.util.Iterator; |
| |
| import org.eclipse.jface.text.DocumentEvent; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IDocumentListener; |
| |
| class PositionTrackerChain implements IDocumentListener { |
| public static final int LINKED_LIST_SIZE = 64; |
| public static final int LINKED_LIST_ENTRY_SIZE = 32; |
| public static int MEMORY_SIZE= 32 + LINKED_LIST_SIZE; |
| |
| private static final int MAX_DEPTH = 100; // 100 saves |
| private static final long MAX_AGE = 24 * 60 * 60 * 1000; // one day |
| |
| private Deque<PositionTracker> fTrackers= new ArrayDeque<>(); |
| private PositionTracker fActiveTracker; |
| private IDocument fDocument; |
| |
| public PositionTrackerChain(long timestamp) { |
| createCheckpoint(timestamp); |
| } |
| |
| public int createCheckpoint(long timestamp) { |
| // Travel in time. |
| while (fActiveTracker != null && fActiveTracker.getTimeStamp() >= timestamp) { |
| fTrackers.removeLast(); |
| if (fTrackers.isEmpty()) { |
| fActiveTracker= null; |
| } else { |
| fActiveTracker= fTrackers.getLast(); |
| fActiveTracker.revive(); |
| } |
| } |
| |
| int retiredMemsize= 0; |
| PositionTracker newTracker= new PositionTracker(); |
| newTracker.setTimeStamp(timestamp); |
| fTrackers.add(newTracker); |
| |
| if (fActiveTracker != null) { |
| fActiveTracker.retire(newTracker); |
| retiredMemsize= fActiveTracker.getMemorySize() + LINKED_LIST_ENTRY_SIZE; |
| } |
| fActiveTracker= newTracker; |
| checkTrackerLimits(); |
| return retiredMemsize; |
| } |
| |
| private void checkTrackerLimits() { |
| while (fTrackers.size() >= MAX_DEPTH) { |
| fTrackers.removeFirst(); |
| } |
| long minTimeStamp= fActiveTracker.getTimeStamp() - MAX_AGE; |
| for (Iterator<PositionTracker> iter = fTrackers.iterator(); iter.hasNext();) { |
| PositionTracker tracker= iter.next(); |
| if (tracker.getRetiredTimeStamp() >= minTimeStamp) { |
| break; |
| } |
| iter.remove(); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.text.IPositionUpdater#update(DocumentEvent) |
| */ |
| private void update(DocumentEvent event) { |
| String text = event.getText(); |
| int insertLen = text != null ? text.length() : 0; |
| update(event.getOffset(), event.getLength(), insertLen); |
| } |
| |
| void update(int offset, int deleteLen, int insertLen) { |
| if (insertLen > deleteLen) { |
| fActiveTracker.insert(offset + deleteLen, insertLen - deleteLen); |
| } else if (insertLen < deleteLen) { |
| fActiveTracker.delete(offset+insertLen, deleteLen - insertLen); |
| } |
| } |
| |
| /** |
| * Finds the nearest tracker created at or after the given time. |
| * |
| * @param timestamp in milliseconds. |
| * @return the tracker nearest to the timestamp, <code>null</code> if all were created before. |
| */ |
| public PositionTracker findTrackerAtOrAfter(long timestamp) { |
| PositionTracker candidate= null; |
| for (Iterator<PositionTracker> iter = fTrackers.descendingIterator(); iter.hasNext();) { |
| PositionTracker tracker = iter.next(); |
| long trackerTimestamp= tracker.getTimeStamp(); |
| if (trackerTimestamp >= timestamp) { |
| candidate= tracker; |
| } else { |
| break; |
| } |
| } |
| return candidate; |
| } |
| |
| /** |
| * Finds the tracker created at the given time. |
| * |
| * @param timestamp in milliseconds. |
| * @return the tracker at the timestamp, <code>null</code> if none created at the given time. |
| */ |
| public PositionTracker findTrackerAt(long timestamp) { |
| for (Iterator<PositionTracker> iter = fTrackers.descendingIterator(); iter.hasNext();) { |
| PositionTracker tracker = iter.next(); |
| long trackerTimestamp= tracker.getTimeStamp(); |
| if (trackerTimestamp == timestamp) { |
| return tracker; |
| } |
| if (trackerTimestamp < timestamp) { |
| return null; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Destroys the tracker. |
| */ |
| public void dispose() { |
| stopTracking(); |
| fTrackers= null; |
| fActiveTracker= null; |
| } |
| |
| public void startTracking(IDocument doc) { |
| stopTracking(); |
| fDocument= doc; |
| if (fDocument != null) { |
| fDocument.addDocumentListener(this); |
| } |
| } |
| |
| public void stopTracking() { |
| if (fDocument != null) { |
| fDocument.removeDocumentListener(this); |
| fDocument= null; |
| } |
| } |
| |
| @Override |
| public void documentAboutToBeChanged(DocumentEvent event) { |
| update(event); |
| } |
| |
| @Override |
| public void documentChanged(DocumentEvent event) { |
| // React before updating the document. |
| } |
| |
| public IDocument getCurrentDocument() { |
| return fDocument; |
| } |
| |
| public PositionTracker getActiveTracker() { |
| return fActiveTracker; |
| } |
| |
| public boolean isModified() { |
| return fTrackers.size() > 1 || fActiveTracker.isModified(); |
| } |
| |
| public int getMemorySize() { |
| int size= MEMORY_SIZE; |
| for (PositionTracker tracker : fTrackers) { |
| size += LINKED_LIST_ENTRY_SIZE; |
| size += tracker.getMemorySize(); |
| } |
| return size; |
| } |
| |
| public int removeOldest() { |
| int memdiff= 0; |
| if (fTrackers.size() > 1) { |
| PositionTracker tracker= fTrackers.removeFirst(); |
| memdiff= tracker.getMemorySize() + LINKED_LIST_ENTRY_SIZE; |
| tracker.clear(); |
| } |
| return -memdiff; |
| } |
| |
| public long getOldestRetirement() { |
| if (fTrackers.size() > 1) { |
| PositionTracker tracker= fTrackers.getFirst(); |
| return tracker.getRetiredTimeStamp(); |
| } |
| return Long.MAX_VALUE; |
| } |
| } |