if (text != null && !text.equals("")) {   position.setText(""); position.length= text.length(); } else RefactoringCorePlugin.logErrorMessage("Dubious undo edit found: " + undo.toString());
position.setText(""); position.length= text.length();
	public final String getPreviewContent(final TextEditBasedChangeGroup[] groups, final IRegion region, final boolean expand, final int surround, final IProgressMonitor monitor) throws CoreException {		final Set cachedGroups= new HashSet(Arrays.asList(groups));		final IDocument document= new Document(getCurrentDocument(monitor).get());		// Marks the region in the document to be previewed		final Position range= new Position(region.getOffset(), region.getLength());		try {			ComposableBufferChange change= null;			final TextEditBasedChangeGroup[] changedGroups= getChangeGroups();			LinkedList compositeUndo= new LinkedList();			for (int index= 0; index < fChanges.size(); index++) {				change= (ComposableBufferChange) fChanges.get(index);				TextEdit copy= null;				try {					// Have to use a copy					fCopier= new TextEditCopier(change.getEdit());					copy= fCopier.perform();					// Create a mapping from the copied edits to its originals					final Map originalMap= new HashMap();					for (final Iterator outer= change.getGroups().iterator(); outer.hasNext();) {						final ComposableBufferChangeGroup group= (ComposableBufferChangeGroup) outer.next();						for (final Iterator inner= group.getCachedEdits().iterator(); inner.hasNext();) {							final TextEdit originalEdit= (TextEdit) inner.next();							final TextEdit copiedEdit= fCopier.getCopy(originalEdit);							if (copiedEdit != null)								originalMap.put(copiedEdit, originalEdit);							else								RefactoringCorePlugin.logErrorMessage("Could not find a copy for the indexed text edit " + originalEdit.toString()); //$NON-NLS-1$						}					}					final ComposableBufferChangeGroup[] currentGroup= { null};					final TextEdit[] currentEdit= { null};					// Text edit processor which sets the change group and					// current edit when processing					final TextEditProcessor processor= new TextEditProcessor(document, copy, TextEdit.NONE) {						protected final boolean considerEdit(final TextEdit edit) {							final TextEdit originalEdit= (TextEdit) originalMap.get(edit);							if (originalEdit != null) {								currentEdit[0]= originalEdit;								boolean found= false;								for (int offset= 0; offset < changedGroups.length && !found; offset++) {									final ComposableBufferChangeGroup group= (ComposableBufferChangeGroup) changedGroups[offset];									if (group.containsEdit(originalEdit)) {										currentGroup[0]= group;										found= true;									}								}								if (!found)									currentGroup[0]= null;							} else if (!(edit instanceof MultiTextEdit)) {								RefactoringCorePlugin.logErrorMessage("Could not find the original of the copied text edit " + edit.toString()); //$NON-NLS-1$							}							return true;						}					};					final LinkedList eventUndos= new LinkedList();					// Listener to record the undos on the document (offsets					// relative to the document event)					final IDocumentListener listener= new IDocumentListener() {						public final void documentAboutToBeChanged(final DocumentEvent event) {							final ComposableUndoEdit edit= new ComposableUndoEdit();							edit.setGroup(currentGroup[0]);							edit.setOriginal(currentEdit[0]);							edit.setUndo(createUndoEdit(document, event.getOffset(), event.getLength(), event.getText()));							eventUndos.addFirst(edit);						}						public final void documentChanged(final DocumentEvent event) {							// Do nothing						}					};					try {						// Record undos in LIFO order						document.addDocumentListener(listener);						processor.performEdits();					} finally {						document.removeDocumentListener(listener);					}					compositeUndo.addFirst(eventUndos);				} finally {					fCopier= null;				}			}			final IPositionUpdater positionUpdater= new IPositionUpdater() {				public final void update(final DocumentEvent event) {					final int eventOffset= event.getOffset();					final int eventLength= event.getLength();					final int eventOldEndOffset= eventOffset + eventLength;					final String eventText= event.getText();					final int eventNewLength= eventText == null ? 0 : eventText.length();					final int eventNewEndOffset= eventOffset + eventNewLength;					final int deltaLength= eventNewLength - eventLength;					try {						final Position[] positions= event.getDocument().getPositions(COMPOSABLE_POSITION_CATEGORY);						for (int index= 0; index < positions.length; index++) {							final Position position= positions[index];							if (position.isDeleted())								continue;							final int offset= position.getOffset();							final int length= position.getLength();							final int end= offset + length;							if (offset > eventOldEndOffset) {								// position comes way after change - shift								position.setOffset(offset + deltaLength);							} else if (end < eventOffset) {								// position comes way before change - leave								// alone							} else if (offset == eventOffset) {								// leave alone, since the edits are overlapping							} else if (offset <= eventOffset && end >= eventOldEndOffset) {								// event completely internal to the position								// -								// adjust length								position.setLength(length + deltaLength);							} else if (offset < eventOffset) {								// event extends over end of position - include								// the								// replacement text into the position								position.setLength(eventNewEndOffset - offset);							} else if (end > eventOldEndOffset) {								// event extends from before position into it -								// adjust								// offset and length, including the replacement								// text into								// the position								position.setOffset(eventOffset);								int deleted= eventOldEndOffset - offset;								position.setLength(length - deleted + eventNewLength);							} else {								// event comprises the position - keep it at the								// same								// position, but always inside the replacement								// text								int newOffset= Math.min(offset, eventNewEndOffset);								int newEndOffset= Math.min(end, eventNewEndOffset);								position.setOffset(newOffset);								position.setLength(newEndOffset - newOffset);							}						}					} catch (BadPositionCategoryException exception) {						// ignore and return					}				}			};			try {				document.addPositionCategory(COMPOSABLE_POSITION_CATEGORY);				document.addPositionUpdater(positionUpdater);				// Apply undos in LIFO order to get to the original document				// Track the undos of edits which are in change groups to be				// previewed and insert				// Undo edits for them (corresponding to the linearized net				// effect on the original document)				final LinkedList undoQueue= new LinkedList();				for (final Iterator outer= compositeUndo.iterator(); outer.hasNext();) {					for (final Iterator inner= ((List) outer.next()).iterator(); inner.hasNext();) {						final ComposableUndoEdit edit= (ComposableUndoEdit) inner.next();						final ReplaceEdit undo= edit.getUndo();						final int offset= undo.getOffset();						final int length= undo.getLength();						final String text= undo.getText();						ComposableEditPosition position= new ComposableEditPosition();						if (cachedGroups.contains(edit.getGroup())) {							if (text == null || text.equals("")) { //$NON-NLS-1$								position.offset= offset;								if (length != 0) {									// Undo is a delete, create final insert									// edit									position.length= 0;									position.setText(edit.getOriginalText());								} else									RefactoringCorePlugin.logErrorMessage("Dubious undo edit found: " + undo.toString()); //$NON-NLS-1$							} else if (length == 0) {								position.offset= offset;								if (text != null && !text.equals("")) { //$NON-NLS-1$									// Undo is an insert, create final delete									// edit									position.setText(""); //$NON-NLS-1$									position.length= text.length();								} else									RefactoringCorePlugin.logErrorMessage("Dubious undo edit found: " + undo.toString()); //$NON-NLS-1$							} else {								// Undo is a replace, create final replace edit								position.offset= offset;								position.length= length;								position.setText(edit.getOriginalText());							}							document.addPosition(COMPOSABLE_POSITION_CATEGORY, position);						}						position= new ComposableEditPosition();						position.offset= undo.getOffset();						position.length= undo.getLength();						position.setText(undo.getText());						undoQueue.add(position);					}					for (final Iterator iterator= undoQueue.iterator(); iterator.hasNext();) {						final ComposableEditPosition position= (ComposableEditPosition) iterator.next();						document.replace(position.offset, position.length, position.getText());						iterator.remove();					}				}				// Use a simple non deleting position updater for the range				final IPositionUpdater markerUpdater= new NonDeletingPositionUpdater(MARKER_POSITION_CATEGORY);				try {					final Position[] positions= document.getPositions(COMPOSABLE_POSITION_CATEGORY);					document.addPositionCategory(MARKER_POSITION_CATEGORY);					document.addPositionUpdater(markerUpdater);					document.addPosition(MARKER_POSITION_CATEGORY, range);					for (int index= 0; index < positions.length; index++) {						final ComposableEditPosition position= (ComposableEditPosition) positions[index];						document.replace(position.offset, position.length, position.getText() != null ? position.getText() : ""); //$NON-NLS-1$					}				} catch (BadPositionCategoryException exception) {					RefactoringCorePlugin.log(exception);				} finally {					document.removePositionUpdater(markerUpdater);					try {						document.removePosition(MARKER_POSITION_CATEGORY, range);						document.removePositionCategory(MARKER_POSITION_CATEGORY);					} catch (BadPositionCategoryException exception) {						// Cannot happen					}				}			} catch (BadPositionCategoryException exception) {				RefactoringCorePlugin.log(exception);			} finally {				document.removePositionUpdater(positionUpdater);				try {					document.removePositionCategory(COMPOSABLE_POSITION_CATEGORY);				} catch (BadPositionCategoryException exception) {					RefactoringCorePlugin.log(exception);				}			}			return getContent(document, new Region(range.offset, range.length), expand, surround);		} catch (MalformedTreeException exception) {			RefactoringCorePlugin.log(exception);		} catch (BadLocationException exception) {			RefactoringCorePlugin.log(exception);		}		return getPreviewDocument(monitor).get();	}