/******************************************************************************* * Copyright (c) 2008 ARM Limited 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: * ARM Limited - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.debug.internal.ui.disassembly.viewer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.debug.ui.disassembly.IDocumentElementContentProvider; import org.eclipse.cdt.debug.ui.disassembly.IDocumentPresentation; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.ui.progress.WorkbenchJob; /** * Manages the mapping between the viewer model and the underlying debug model * through the content, label and annotation adapters. * Maintains the list of model proxies and reacts to the debug model changes. */ public class DocumentContentProvider implements IModelChangedListener { private VirtualSourceViewer fViewer; private VirtualDocument fDocument; private Object fRoot; private Object fBase; private Object fInput; private IModelProxy fRootProxy; private IModelProxy fBaseProxy; private List<IModelProxy> fLineProxies = new ArrayList<IModelProxy>( 50 ); private Map<Object, Integer> fLineElements = new HashMap<Object, Integer>( 20 ); private DocumentUpdate fUpdateInProgress; public DocumentContentProvider( VirtualDocument document ) { super(); fDocument = document; } protected void init( Object root ) { fRoot = root; installRootProxy( fRoot ); } public void update( IDocumentPresentation presentationContext, int lineCount, int offset, boolean reveal ) { IDocumentElementContentProvider contentAdapter = getContentAdapter( getInput() ); if ( contentAdapter != null && getRoot() != null && getBase() != null ) { DocumentContentUpdate update = new DocumentContentUpdate( this, contentAdapter, presentationContext, getRoot(), getBase(), getInput(), lineCount, offset, reveal ); schedule( update ); } else { updateCompleted( new DocumentContentUpdate( this, contentAdapter, presentationContext, getRoot(), getBase(), getInput(), lineCount, offset, reveal ) ); } } public synchronized void updateCompleted( DocumentContentUpdate update ) { if ( fUpdateInProgress == update ) { fUpdateInProgress = null; } if ( !update.isCanceled() ) { disposeLineProxies(); fLineElements.clear(); getDocument().setCurrentOffset( update.getOffset() ); Object[] elements = update.getElements(); for ( int i = 0; i < elements.length; ++i ) { fLineElements.put( elements[i], Integer.valueOf( i ) ); installLineProxy( i, elements[i] ); getDocument().updateElement( getInput(), i, elements[i] ); } } // TODO: display error content if status is not OK } protected IDocumentElementContentProvider getContentAdapter( Object element ) { IDocumentElementContentProvider adapter = null; if ( element instanceof IDocumentElementContentProvider ) { adapter = (IDocumentElementContentProvider)element; } else if ( element instanceof IAdaptable ) { IAdaptable adaptable = (IAdaptable)element; adapter = (IDocumentElementContentProvider)adaptable.getAdapter( IDocumentElementContentProvider.class ); } return adapter; } protected VirtualDocument getDocument() { return fDocument; } public Object getRoot() { return fRoot; } public Object getInput() { return fInput; } public Object getBase() { return fBase; } public void dispose() { synchronized( this ) { if ( fUpdateInProgress != null ) { fUpdateInProgress.cancel(); } } disposeRootProxy(); fDocument = null; fInput = null; fViewer = null; } public void changeInput( VirtualSourceViewer viewer, IDocumentPresentation presentationContext, Object oldInput, Object newInput, int offset ) { fViewer = viewer; fInput = newInput; IDocumentElementContentProvider contentAdapter = getContentAdapter( getInput() ); if ( contentAdapter != null ) { DocumentBaseChangeUpdate update = new DocumentBaseChangeUpdate( this, contentAdapter, presentationContext, getRoot(), getBase(), getInput(), offset ); schedule( update ); } else { inputChanged( new DocumentBaseChangeUpdate( this, contentAdapter, presentationContext, getRoot(), getBase(), getInput(), offset ) ); } } public synchronized void inputChanged( DocumentBaseChangeUpdate update ) { if ( fUpdateInProgress == update ) { fUpdateInProgress = null; } Object newBase = update.getBaseElement(); int newOffset = update.getOffset(); VirtualDocument document = getDocument(); if ( document != null ) { boolean needsUpdate = false; if ( newBase != getBase() ) { fBase = newBase; disposeBaseProxy(); installBaseProxy( fBase ); needsUpdate = true; } if ( newOffset != document.getCurrentOffset() ) { document.setCurrentOffset( newOffset ); needsUpdate = true; } if ( needsUpdate ) { WorkbenchJob job = new WorkbenchJob( "refresh content" ) { //$NON-NLS-1$ /* (non-Javadoc) * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus runInUIThread( IProgressMonitor monitor ) { getViewer().refresh( true ); return Status.OK_STATUS; } }; job.setSystem( true ); job.schedule(); } } } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener#modelChanged(org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta, org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy) */ public void modelChanged( final IModelDelta delta, final IModelProxy proxy ) { WorkbenchJob job = new WorkbenchJob( "process model delta" ) { //$NON-NLS-1$ /* (non-Javadoc) * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus runInUIThread( IProgressMonitor monitor ) { if ( !proxy.isDisposed() ) { handleModelChanges( delta ); } return Status.OK_STATUS; } }; job.setSystem( true ); job.schedule(); } protected void handleModelChanges( IModelDelta delta ) { updateNodes( new IModelDelta[] { delta } ); } protected void updateNodes( IModelDelta[] nodes ) { for( int i = 0; i < nodes.length; i++ ) { IModelDelta node = nodes[i]; int flags = node.getFlags(); if ( (flags & IModelDelta.ADDED) != 0 ) { handleAdd( node ); } if ( (flags & IModelDelta.REMOVED) != 0 ) { handleRemove( node ); } if ( (flags & IModelDelta.CONTENT) != 0 ) { handleContent( node ); } if ( (flags & IModelDelta.SELECT) != 0 ) { handleSelect( node ); } if ( (flags & IModelDelta.STATE) != 0 ) { handleState( node ); } if ( (flags & IModelDelta.INSERTED) != 0 ) { handleInsert( node ); } if ( (flags & IModelDelta.REPLACED) != 0 ) { handleReplace( node ); } if ( (flags & IModelDelta.INSTALL) != 0 ) { handleInstall( node ); } if ( (flags & IModelDelta.UNINSTALL) != 0 ) { handleUninstall( node ); } if ( (flags & IModelDelta.REVEAL) != 0 ) { handleReveal( node ); } updateNodes( node.getChildDeltas() ); } } protected void handleState( IModelDelta delta ) { int index = getElementIndex( delta.getElement() ); if ( index >= 0 ) { getDocument().updateElement( getInput(), index, delta.getElement() ); } } protected void handleSelect( IModelDelta delta ) { } protected void handleContent( IModelDelta delta ) { if ( delta.getElement().equals( getRoot() ) || delta.getElement().equals( getBase() ) ) { getViewer().refresh(); } } protected void handleRemove( IModelDelta delta ) { } protected void handleAdd( IModelDelta delta ) { } protected void handleInsert( IModelDelta delta ) { } protected void handleReplace( IModelDelta delta ) { } protected void handleReveal( IModelDelta delta ) { } protected void handleInstall( IModelDelta delta ) { } protected void handleUninstall( IModelDelta delta ) { } protected synchronized void installRootProxy( Object element ) { if ( element != null && (!element.equals( getRoot()) || fRootProxy == null) ) { disposeRootProxy(); IModelProxyFactory modelProxyFactory = getModelProxyFactoryAdapter( element ); if ( modelProxyFactory != null ) { final IModelProxy proxy = modelProxyFactory.createModelProxy( element, getPresentationContext() ); if ( proxy != null ) { fRootProxy = proxy; Job job = new Job( "Model Proxy installed notification job" ) {//$NON-NLS-1$ /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IStatus run( IProgressMonitor monitor ) { if ( !monitor.isCanceled() ) { proxy.init( getPresentationContext() ); proxy.addModelChangedListener( DocumentContentProvider.this ); proxy.installed( getViewer() ); } return Status.OK_STATUS; } }; job.setSystem( true ); job.schedule(); } } } } protected synchronized void installBaseProxy( Object element ) { if ( element != null && (!element.equals( getBase()) || fBaseProxy == null) ) { IModelProxyFactory modelProxyFactory = getModelProxyFactoryAdapter( element ); if ( modelProxyFactory != null ) { final IModelProxy proxy = modelProxyFactory.createModelProxy( element, getPresentationContext() ); if ( proxy != null ) { fBaseProxy = proxy; Job job = new Job( "Model Proxy installed notification job" ) {//$NON-NLS-1$ /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IStatus run( IProgressMonitor monitor ) { if ( !monitor.isCanceled() ) { proxy.init( getPresentationContext() ); proxy.addModelChangedListener( DocumentContentProvider.this ); proxy.installed( getViewer() ); } return Status.OK_STATUS; } }; job.setSystem( true ); job.schedule(); } } } } protected synchronized void installLineProxy( int index, Object element ) { IModelProxyFactory modelProxyFactory = getModelProxyFactoryAdapter( element ); if ( modelProxyFactory != null ) { final IModelProxy proxy = modelProxyFactory.createModelProxy( element, getPresentationContext() ); if ( proxy != null ) { fLineProxies.add( index, proxy ); Job job = new Job( "Model Proxy installed notification job" ) {//$NON-NLS-1$ /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IStatus run( IProgressMonitor monitor ) { if ( !monitor.isCanceled() ) { proxy.init( getPresentationContext() ); proxy.addModelChangedListener( DocumentContentProvider.this ); proxy.installed( getViewer() ); } return Status.OK_STATUS; } }; job.setSystem( true ); job.schedule(); } } } protected IModelProxyFactory getModelProxyFactoryAdapter( Object element ) { IModelProxyFactory adapter = null; if ( element instanceof IModelProxyFactory ) { adapter = (IModelProxyFactory)element; } else if ( element instanceof IAdaptable ) { IAdaptable adaptable = (IAdaptable)element; adapter = (IModelProxyFactory)adaptable.getAdapter( IModelProxyFactory.class ); } return adapter; } protected IPresentationContext getPresentationContext() { return getDocument().getPresentationContext(); } protected synchronized void disposeRootProxy() { disposeBaseProxy(); if ( fRootProxy != null ) { fRootProxy.dispose(); } fRootProxy = null; } protected synchronized void disposeBaseProxy() { disposeLineProxies(); if ( fBaseProxy != null ) { fBaseProxy.dispose(); } fBaseProxy = null; } protected synchronized void disposeLineProxies() { for ( IModelProxy proxy : fLineProxies ) { proxy.dispose(); } fLineProxies.clear(); } protected VirtualSourceViewer getViewer() { return fViewer; } synchronized void schedule( DocumentUpdate update ) { if ( fUpdateInProgress != null ) { if ( update instanceof DocumentBaseChangeUpdate ) { // cancel the earlier update and start the latest fUpdateInProgress.cancel(); fUpdateInProgress.done(); fUpdateInProgress = update; fUpdateInProgress.start(); } else if ( fUpdateInProgress instanceof DocumentBaseChangeUpdate && update instanceof DocumentContentUpdate ) { // cancel the content update because the base change update // will start a new one update.cancel(); update.done(); } else if ( fUpdateInProgress instanceof DocumentContentUpdate && update instanceof DocumentBaseChangeUpdate ) { // cancel the content update and start the base change update fUpdateInProgress.cancel(); fUpdateInProgress.done(); fUpdateInProgress = update; fUpdateInProgress.start(); } } else { fUpdateInProgress = update; fUpdateInProgress.start(); } } private int getElementIndex( Object element ) { Integer index = fLineElements.get( element ); return ( index != null ) ? index.intValue() : -1; } protected Object getElementAtLine( int lineNumber ) { synchronized( fLineElements ) { for ( Object element : fLineElements.keySet() ) { if ( fLineElements.get( element ).intValue() == lineNumber ) { return element; } } } return null; } }