| /******************************************************************************* |
| * Copyright (c) 2006 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.html.ui.internal.projection; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.jface.text.DocumentEvent; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IDocumentExtension; |
| import org.eclipse.jface.text.IDocumentListener; |
| import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; |
| import org.eclipse.jface.text.source.projection.ProjectionViewer; |
| import org.eclipse.wst.html.ui.internal.Logger; |
| |
| /** |
| * Contains information about a projection viewer and also manages updating |
| * the viewer's projection annotation model |
| */ |
| class ProjectionViewerInformation { |
| // copies of this class located in: |
| // org.eclipse.wst.xml.ui.internal.projection |
| // org.eclipse.wst.css.ui.internal.projection |
| // org.eclipse.wst.html.ui.internal.projection |
| // org.eclipse.jst.jsp.ui.internal.projection |
| |
| /** |
| * Listens to document to be aware of when to update the projection |
| * annotation model. |
| */ |
| private class DocumentListener implements IDocumentListener { |
| private ProjectionViewerInformation fInfo; |
| |
| public DocumentListener(ProjectionViewerInformation info) { |
| fInfo = info; |
| } |
| |
| public void documentAboutToBeChanged(DocumentEvent event) { |
| IDocument document = event.getDocument(); |
| if (fInfo.getDocument() == document) { |
| fInfo.setIsDocumentChanging(true); |
| } |
| } |
| |
| public void documentChanged(DocumentEvent event) { |
| // register a post notification replace so that projection |
| // annotation model will be updated after all documentChanged |
| // listeners have been notified |
| IDocument document = event.getDocument(); |
| if (document instanceof IDocumentExtension && fInfo.getDocument() == document) { |
| if (fInfo.hasChangesQueued()) |
| ((IDocumentExtension) document).registerPostNotificationReplace(this, new PostDocumentChangedListener(fInfo)); |
| } |
| } |
| } |
| |
| /** |
| * Essentially a post document changed listener because it is called after |
| * documentchanged has been fired. |
| */ |
| private class PostDocumentChangedListener implements IDocumentExtension.IReplace { |
| private ProjectionViewerInformation fInfo; |
| |
| public PostDocumentChangedListener(ProjectionViewerInformation info) { |
| fInfo = info; |
| } |
| |
| public void perform(IDocument document, IDocumentListener owner) { |
| fInfo.applyAnnotationModelChanges(); |
| fInfo.setIsDocumentChanging(false); |
| } |
| } |
| |
| /** |
| * Projection annotation model current associated with this projection |
| * viewer |
| */ |
| private ProjectionAnnotationModel fProjectionAnnotationModel; |
| /** |
| * Document currently associated with this projection viewer |
| */ |
| private IDocument fDocument; |
| /** |
| * Listener to fProjectionViewer's document |
| */ |
| private IDocumentListener fDocumentListener; |
| /** |
| * Indicates whether or not document is in the middle of changing |
| */ |
| private boolean fIsDocumentChanging = false; |
| /** |
| * List of projection annotation model changes that need to be applied |
| */ |
| private List fQueuedAnnotationChanges; |
| |
| public ProjectionViewerInformation(ProjectionViewer viewer) { |
| fDocument = viewer.getDocument(); |
| fProjectionAnnotationModel = viewer.getProjectionAnnotationModel(); |
| } |
| |
| IDocument getDocument() { |
| return fDocument; |
| } |
| |
| private List getQueuedAnnotationChanges() { |
| if (fQueuedAnnotationChanges == null) { |
| fQueuedAnnotationChanges = new ArrayList(); |
| } |
| return fQueuedAnnotationChanges; |
| } |
| |
| void setIsDocumentChanging(boolean changing) { |
| fIsDocumentChanging = changing; |
| } |
| |
| private boolean isDocumentChanging() { |
| return fIsDocumentChanging; |
| } |
| |
| /** |
| * Applies the pending projection annotation model changes to the |
| * projection annotation model. |
| */ |
| void applyAnnotationModelChanges() { |
| List queuedChanges = getQueuedAnnotationChanges(); |
| // go through all the pending annotation changes and apply |
| // them to |
| // the projection annotation model |
| while (!queuedChanges.isEmpty()) { |
| ProjectionAnnotationModelChanges changes = (ProjectionAnnotationModelChanges) queuedChanges.remove(0); |
| try { |
| fProjectionAnnotationModel.modifyAnnotations(changes.getDeletions(), changes.getAdditions(), changes.getModifications()); |
| } |
| catch (Exception e) { |
| // if anything goes wrong, log it and continue |
| Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e); |
| } |
| } |
| } |
| |
| /** |
| * Returns true if there are annotation changes queued up, false otherwise |
| * |
| * @return boolean |
| */ |
| boolean hasChangesQueued() { |
| return !getQueuedAnnotationChanges().isEmpty(); |
| } |
| |
| /** |
| * Updates projection annotation model if document is not in flux. |
| * Otherwise, queues up the changes to be applied when document is ready. |
| */ |
| public void queueAnnotationModelChanges(ProjectionAnnotationModelChanges newChange) { |
| /* |
| * future_TODO: maybe improve by checking if annotation projection |
| * model change already exists for node. if so, throw out old change. |
| */ |
| getQueuedAnnotationChanges().add(newChange); |
| |
| // if document isn't changing, go ahead and apply it |
| if (!isDocumentChanging()) { |
| applyAnnotationModelChanges(); |
| } |
| } |
| |
| public void initialize() { |
| // add document listener |
| if (fDocumentListener == null) { |
| fDocumentListener = new DocumentListener(this); |
| } |
| getDocument().addDocumentListener(fDocumentListener); |
| } |
| |
| public void dispose() { |
| // remove document listener |
| if (fDocumentListener != null) { |
| getDocument().removeDocumentListener(fDocumentListener); |
| } |
| |
| // clear out list of queued changes since it may no longer |
| // be accurate |
| if (fQueuedAnnotationChanges != null) { |
| fQueuedAnnotationChanges.clear(); |
| fQueuedAnnotationChanges = null; |
| } |
| } |
| } |