alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*******************************************************************************
 * Copyright (c) 2000, 2007 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.team.internal.ccvs.ui;

import java.io.*;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.ISubscriberChangeEvent;
import org.eclipse.team.core.subscribers.ISubscriberChangeListener;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.editors.text.IStorageDocumentProvider;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.texteditor.*;
import org.eclipse.ui.texteditor.quickdiff.IQuickDiffReferenceProvider;

/**
 * A QuickDiff provider that provides a reference to the latest revision of a file
 * in the CVS repository. The provider notifies when the file's sync state changes
 * and the diff should be recalculated (e.g. commit, update...) or when the file
 * is changed (e.g. replace with).
 * 
 * Here are the file states and what this provider does for each:
 * 
 * 1. File is unmanaged : reference == empty document
 * 2. Unmanaged file transitions to managed : empty reference updated with new remote revision 
 * 3. A managed file has new remote (commit, refresh remote) : reference updated with new 
 * remote revision
 * 4. A managed file cleaned, remote is the same (replace with, update) : refresh diff bar 
 * with existing reference 
 * 
 * [Note: Currently an empty document must be returned for an unmanaged file. This
 * results in the entire document appearing as outgoing changes in the quickdiff bar. 
 * This is required because the quickdiff support relies on IDocument change events
 * to update the quickdiff, and returning null for the reference document doesn't
 * allow the transition to later return a IDocument.]
 * 
 * @since 3.0
 */
public class RemoteRevisionQuickDiffProvider implements IQuickDiffReferenceProvider {
	// The editor showing this quickdiff and provides access to the editor input and
	// ultimatly the IFile.
	private ITextEditor fEditor = null;	
	
	// The document containing the remote file. Can be null if the assigned editor doesn't have
	// a CVS remote resource associated with it.
	private IDocument fReference = null;
	
	// Will be true when the document has been read and initialized.
	private boolean fReferenceInitialized = false;
	
	// Document provider allows us to register/deregister the element state change listener.
	private IDocumentProvider fDocumentProvider = null;

	// Unique id for this reference provider as set via setId(). 
	private String fId;
	
	// A handle to the remote CVS file for this provider. 
	private SyncInfo fLastSyncState;

	// Job that re-creates the reference document.
	private Job fUpdateJob;
	
	private boolean DEBUG = false;
	
	/**
	 * Updates the document if a sync changes occurs to the associated CVS file.
	 */
	private ISubscriberChangeListener teamChangeListener = new ISubscriberChangeListener() {
		public void subscriberResourceChanged(ISubscriberChangeEvent[] deltas) {
			if(fReferenceInitialized) {
				for (int i = 0; i < deltas.length; i++) {
					ISubscriberChangeEvent delta = deltas[i];
					IResource resource = delta.getResource();
					if(resource.getType() == IResource.FILE && 
					   fLastSyncState != null && resource.equals(fLastSyncState.getLocal())) {
						if(delta.getFlags() == ISubscriberChangeEvent.SYNC_CHANGED) {
							fetchContentsInJob();
						}
					} 
				}
			}
		}
	};

	/**
	 * Updates the document if the document is changed (e.g. replace with)
	 */
	private IElementStateListener documentListener = new IElementStateListener() {
		public void elementDirtyStateChanged(Object element, boolean isDirty) {
		}

		public void elementContentAboutToBeReplaced(Object element) {
		}

		public void elementContentReplaced(Object element) {
			if(fEditor != null && fEditor.getEditorInput() == element) {
				fetchContentsInJob();
			}
		}

		public void elementDeleted(Object element) {
		}

		public void elementMoved(Object originalElement, Object movedElement) {
		}
	};

	/*
	 * @see org.eclipse.test.quickdiff.DocumentLineDiffer.IQuickDiffReferenceProvider#getReference()
	 */
	public IDocument getReference(IProgressMonitor monitor) throws CoreException {
		if(! fReferenceInitialized) return null;
		if (fReference == null) {
			readDocument(monitor);
		}
		return fReference;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.texteditor.quickdiff.IQuickDiffProviderImplementation#setActiveEditor(org.eclipse.ui.texteditor.ITextEditor)
	 */
	public void setActiveEditor(ITextEditor targetEditor) {
		IEditorInput editorInput = targetEditor.getEditorInput();
        if (editorInput == null || ResourceUtil.getFile(editorInput) == null) return;
		fEditor = targetEditor;
		fDocumentProvider= fEditor.getDocumentProvider();
		
		if(fDocumentProvider != null) {
			CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().addListener(teamChangeListener);
			fDocumentProvider.addElementStateListener(documentListener);
		}	
		fReferenceInitialized= true;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.texteditor.quickdiff.IQuickDiffProviderImplementation#isEnabled()
	 */
	public boolean isEnabled() {
		if (! fReferenceInitialized)
			return false;
		try {
			return getManagedCVSFile() != null;
		} catch (CVSException e) {
			return false;
		}
	}

	/*
	 * @see org.eclipse.jface.text.source.diff.DocumentLineDiffer.IQuickDiffReferenceProvider#dispose()
	 */
	public void dispose() {
		fReferenceInitialized = false;
		// stop update job
		if(fUpdateJob != null && fUpdateJob.getState() != Job.NONE) {
			fUpdateJob.cancel();
		}
		
		// remove listeners
		if(fDocumentProvider != null) {
			fDocumentProvider.removeElementStateListener(documentListener);
		}
		CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().removeListener(teamChangeListener);				
	}

	/*
	 * @see org.eclipse.quickdiff.QuickDiffTestPlugin.IQuickDiffProviderImplementation#setId(java.lang.String)
	 */
	public void setId(String id) {
		fId= id;
	}

	/*
	 * @see org.eclipse.jface.text.source.diff.DocumentLineDiffer.IQuickDiffReferenceProvider#getId()
	 */
	public String getId() {
		return fId;
	}
	
	/**
	 * Determine if the file represented by this quickdiff provider has changed with
	 * respect to it's remote state. Return true if the remote contents should be
	 * refreshed, and false if not.
	 */
	private boolean computeChange(IProgressMonitor monitor) throws TeamException {
		boolean needToUpdateReferenceDocument = false;
		if(fReferenceInitialized) {
			SyncInfo info = getSyncState(getFileFromEditor());	
			if(info == null && fLastSyncState != null) {
				return true;
			} else if(info == null) {
				return false;
			}
					
			if(fLastSyncState == null) {
				needToUpdateReferenceDocument = true;
			} else if(! fLastSyncState.equals(info)) {
				needToUpdateReferenceDocument = true; 
			}
			if(DEBUG) debug(fLastSyncState, info);
			fLastSyncState = info;
		}		
		return needToUpdateReferenceDocument;		
	}
	
	private void debug(SyncInfo lastSyncState, SyncInfo info) {
		String last = "[none]"; //$NON-NLS-1$
		if(lastSyncState != null) {
			last = lastSyncState.toString();
		}
		System.out.println("+ CVSQuickDiff: was " + last + " is " + info.toString()); //$NON-NLS-1$ //$NON-NLS-2$
	}

	private SyncInfo getSyncState(IResource resource) throws TeamException {
		if (resource == null) return null;
		return CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().getSyncInfo(resource);
	}
	
	/**
	 * Creates a document and initializes it with the contents of a CVS remote
	 * resource.
	 * @param monitor the progress monitor
	 * @throws CoreException
	 */
	private void readDocument(IProgressMonitor monitor) throws CoreException {
		if(! fReferenceInitialized) return;
		if(fReference == null)
			fReference = new Document();
		if(computeChange(monitor)) {
			ICVSRemoteFile remoteFile = (ICVSRemoteFile)fLastSyncState.getRemote(); 
			if (fLastSyncState.getRemote() != null && fDocumentProvider instanceof IStorageDocumentProvider) {
				IStorageDocumentProvider provider= (IStorageDocumentProvider) fDocumentProvider;			
				String encoding= provider.getEncoding(fEditor.getEditorInput());
				if (encoding == null) {
					encoding= provider.getDefaultEncoding();
				}
				if(monitor.isCanceled()) return;
				InputStream stream= remoteFile.getContents(monitor);
				if (stream == null || monitor.isCanceled() || ! fReferenceInitialized) {
					return;
				}
				setDocumentContent(fReference, stream, encoding);
			} else {
				// the remote is null, so ensure that the document is null
				if(monitor.isCanceled()) return;
				fReference.set(""); //$NON-NLS-1$
			}
			if(DEBUG) System.out.println("+ CVSQuickDiff: updating document " + (fReference!=null ? "remote found" : "remote empty")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
	}
	
	/**
	 * Intitializes the given document with the given stream using the given encoding.
	 *
	 * @param document the document to be initialized
	 * @param contentStream the stream which delivers the document content
	 * @param encoding the character encoding for reading the given stream
	 * @exception CoreException if the given stream can not be read
	 */
	private static void setDocumentContent(IDocument document, InputStream contentStream, String encoding) throws CoreException {
		Reader in= null;
		try {
			final int DEFAULT_FILE_SIZE= 15 * 1024;

			in= new BufferedReader(new InputStreamReader(contentStream, encoding), DEFAULT_FILE_SIZE);
			CharArrayWriter caw= new CharArrayWriter(DEFAULT_FILE_SIZE);
			char[] readBuffer= new char[2048];
			int n= in.read(readBuffer);
			while (n > 0) {
				caw.write(readBuffer, 0, n);
				n= in.read(readBuffer);
			}
			document.set(caw.toString());
			//System.out.println("+ CVSQuickDiff: updating document : " + caw.toString());
		} catch (IOException x) {
			IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, CVSUIMessages.RemoteRevisionQuickDiffProvider_readingFile, x);
			throw new CVSException(status); 
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException x) {
					IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, CVSUIMessages.RemoteRevisionQuickDiffProvider_closingFile, x);
					throw new CVSException(status); 
				}
			}
		}
	}
	
	/**
	 * Returns the ICVSFile associated with he active editor or <code>null 
	 * if the provider doesn't not have access to a CVS managed file.
	 * @return the handle to a CVS file
	 */
	private ICVSFile getManagedCVSFile() throws CVSException {
		if(fEditor != null) {
			IFile file = getFileFromEditor();
				if(file != null && CVSWorkspaceRoot.isSharedWithCVS(file)) {
					return CVSWorkspaceRoot.getCVSFileFor(file);
				}
		}
		return null;
	}

	private IFile getFileFromEditor() {
		if(fEditor != null) {
			IEditorInput input= fEditor.getEditorInput();
            if (input != null) {
                IFile file = ResourceUtil.getFile(input);
                return file;
            }
		}
		return null;
	}
	
	/**
	 * Runs a job that updates the document. If a previous job is already running it
	 * is stopped before the new job can start.
	 */
	private void fetchContentsInJob() {
		if(! fReferenceInitialized) return;
		if(fUpdateJob != null && fUpdateJob.getState() != Job.NONE) {
			fUpdateJob.cancel();
		}
		fUpdateJob = new Job(CVSUIMessages.RemoteRevisionQuickDiffProvider_fetchingFile) { 
			protected IStatus run(IProgressMonitor monitor) {
				try {
					readDocument(monitor);
				} catch (CoreException e) {
					// continue and return ok for now. The error will be reported
					// when the quick diff supports calls getReference() again.
					// continue
				}
				return Status.OK_STATUS;
			}
		};
		fUpdateJob.schedule();
	}
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.