/******************************************************************************* * Copyright (c) 2006 Business Objects Software Limited and others. * All rights reserved. * This file is 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: * Business Objects Software Limited - initial API and implementation based on Eclipse 3.1.2 code for * /org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/JavaSourceViewer.java * Eclipse source is available at: http://www.eclipse.org/downloads/ *******************************************************************************/ /* * PartiallySynchronizedDocument.java * Creation date: Feb 9, 2006. * By: Edward Lam */ package org.openquark.cal.eclipse.ui.caleditor; import java.util.ArrayList; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IPositionUpdater; import org.eclipse.jface.text.ISynchronizable; import org.eclipse.jface.text.Position; /** * Document that can also be used by a background reconciler. * @author Edward Lam */ public class PartiallySynchronizedDocument extends Document implements ISynchronizable, IPositionUpdater { private final Object fInternalLockObject = new Object(); private Object fLockObject; private Document originalDocument = new Document(); private boolean wasInitialized = false; private ArrayList<Change> changes = new ArrayList<Change>(); { this.addPositionUpdater(this); } /** * Keeps track of an individual changes to the text in the editor. * @author Greg McClement */ private class Change{ private final int offset; private final int oldLength; private final int newLength; public Change(int offset, int oldLength, int newLength){ this.offset = offset; this.oldLength = oldLength; this.newLength = newLength; } public int getOffset(){ return offset; } public int getOldLength(){ return oldLength; } public int getNewLength(){ return newLength; } } public void update(DocumentEvent event) { if (wasInitialized){ int newLength = 0; // although not documented this can be null for some cases. String newText = event.getText(); if (newText != null){ newLength = newText.length(); } changes.add(new Change(event.getOffset(), event.getLength(), newLength)); } else{ wasInitialized = true; } } /** * Document was saved so let's reinitialize everything so * the changes list does not become bloated. If the * changes list gets bloated then we will get less * sleep because of the complaining. */ public void wasSaved(){ originalDocument.set(get()); changes.clear(); } public IDocument getOriginalDocument(){ return originalDocument; } /** * Returns the offset in the original file. If the current offset is in newly added text * then the offset returned is -1 since that would not apply to the original text. */ public int getOriginalOffset(int offset){ for(int i = changes.size() - 1; i >= 0; --i){ Change change = changes.get(i); if (change.getOffset() > offset){ continue; } offset = offset - change.getNewLength() + change.getOldLength(); if (change.getOffset() > offset) { // if after the calculation the offset is on the other side of the change, then the given offset // is actually *in* the change, so return -1 as per spec return -1; } } return offset; } /** * Returns the offset in the original file. If the current offset is in newly added text * then the offset returned is the start of the block that the next text is inserted in. */ public int estimateOriginalOffset(int offset){ for(int i = changes.size() - 1; i >= 0; --i){ Change change = changes.get(i); if (change.getOffset() > offset){ continue; } offset = offset - change.getNewLength() + change.getOldLength(); if (change.getOffset() > offset) { // The character before the change. offset = change.getOffset(); } } return offset; } /** * Converts the given offset to an offset in the current version of the * document. * @param offset offset in the original document * @return the offset in the current version of the document */ public int fromOriginalOffset(int offset){ for(Change change : changes){ if (change.getOffset() > offset){ continue; } offset = offset - change.getOldLength() + change.getNewLength(); if (change.getOffset() > offset) { // if after the calculation the offset is on the other side of the change, then the given offset // is actually *in* the change, so return -1 as per spec return -1; } } return offset; } /* * @see org.eclipse.jface.text.ISynchronizable#setLockObject(java.lang.Object) */ public void setLockObject(Object lockObject) { fLockObject = lockObject; } /* * @see org.eclipse.jface.text.ISynchronizable#getLockObject() */ public Object getLockObject() { return fLockObject == null ? fInternalLockObject : fLockObject; } /* * @see IDocumentExtension#startSequentialRewrite(boolean) */ @Override public void startSequentialRewrite(boolean normalized) { synchronized (getLockObject()) { super.startSequentialRewrite(normalized); } } /* * @see IDocumentExtension#stopSequentialRewrite() */ @Override public void stopSequentialRewrite() { synchronized (getLockObject()) { super.stopSequentialRewrite(); } } /* * @see IDocument#get() */ @Override public String get() { synchronized (getLockObject()) { return super.get(); } } /* * @see IDocument#get(int, int) */ @Override public String get(int offset, int length) throws BadLocationException { synchronized (getLockObject()) { return super.get(offset, length); } } /* * @see IDocument#getChar(int) */ @Override public char getChar(int offset) throws BadLocationException { synchronized (getLockObject()) { return super.getChar(offset); } } /* * @see org.eclipse.jface.text.IDocumentExtension4#getModificationStamp() * @since 3.1 */ @Override public long getModificationStamp() { synchronized (getLockObject()) { return super.getModificationStamp(); } } /* * @see IDocument#replace(int, int, String) */ @Override public void replace(int offset, int length, String text) throws BadLocationException { synchronized (getLockObject()) { super.replace(offset, length, text); } } /* * @see IDocumentExtension4#replace(int, int, String, long) */ @Override public void replace(int offset, int length, String text, long modificationStamp) throws BadLocationException { synchronized (getLockObject()) { super.replace(offset, length, text, modificationStamp); } } /* * @see IDocument#set(String) */ @Override public void set(String text) { synchronized (getLockObject()) { super.set(text); originalDocument.set(text); } } /* * @see IDocumentExtension4#set(String, long) */ @Override public void set(String text, long modificationStamp) { synchronized (getLockObject()) { super.set(text, modificationStamp); originalDocument.set(text, modificationStamp); } } /* * @see org.eclipse.jface.text.AbstractDocument#addPosition(java.lang.String, org.eclipse.jface.text.Position) */ @Override public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException { synchronized (getLockObject()) { super.addPosition(category, position); } } /* * @see org.eclipse.jface.text.AbstractDocument#removePosition(java.lang.String, org.eclipse.jface.text.Position) */ @Override public void removePosition(String category, Position position) throws BadPositionCategoryException { synchronized (getLockObject()) { super.removePosition(category, position); } } /* * @see org.eclipse.jface.text.AbstractDocument#getPositions(java.lang.String) */ @Override public Position[] getPositions(String category) throws BadPositionCategoryException { synchronized (getLockObject()) { return super.getPositions(category); } } }