/*=============================================================================# # Copyright (c) 2007-2016 Stephan Wahlbrink (WalWare.de) 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.ecommons.ltk.ui; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import de.walware.ecommons.FastList; import de.walware.ecommons.IDisposable; import de.walware.ecommons.ltk.AstInfo; import de.walware.ecommons.ltk.ElementChangedEvent; import de.walware.ecommons.ltk.IElementChangedListener; import de.walware.ecommons.ltk.IModelElementDelta; import de.walware.ecommons.ltk.IModelManager; import de.walware.ecommons.ltk.WorkingContext; import de.walware.ecommons.ltk.core.model.ISourceUnit; /** * Controller implementation for input of a part and its model updates. */ public class ElementInfoController implements IModelElementInputProvider, IDisposable { private static int NEWINPUT_DELAY = 100; private final IModelManager fModelProvider; private final WorkingContext fModelContext; private final IElementChangedListener fElementChangeListener; private final FastList<IModelElementInputListener> fListenerList = new FastList<>(IModelElementInputListener.class); private final FastList<IModelElementInputListener> fNewListenerList = new FastList<>(IModelElementInputListener.class); private volatile ISourceUnit fInput; private final Object fInputLock = new Object(); private ISourceUnit fNewInput; private final NewInputUpdater fNewInputJob = new NewInputUpdater(); private class NewInputUpdater extends Job implements ISchedulingRule { public NewInputUpdater() { super("ViewPart Model Element Updater"); // //$NON-NLS-1$ setPriority(Job.SHORT); setRule(this); setSystem(true); setUser(false); } @Override public boolean contains(final ISchedulingRule rule) { return (rule == this); } @Override public boolean isConflicting(final ISchedulingRule rule) { return (rule == this); } @Override protected IStatus run(final IProgressMonitor monitor) { ISourceUnit input; IModelElementInputListener[] listeners; synchronized (fInputLock) { if (monitor.isCanceled() || (fInput == null && fNewInput == null)) { return Status.CANCEL_STATUS; } if (fNewInput == null) { listeners = checkNewListeners(); } else { final AstInfo astInfo = fNewInput.getAstInfo(null, false, null); if (astInfo == null || (astInfo.getLevel() & AstInfo.DEFAULT_LEVEL_MASK) < 1) { return Status.CANCEL_STATUS; } fInput = fNewInput; fNewInput = null; checkNewListeners(); listeners = fListenerList.toArray(); } input = fInput; } if (listeners != null && listeners.length > 0) { notifyInitial(listeners, input, monitor); } return Status.OK_STATUS; } @Override protected void canceling() { fNotifyMonitor.setCanceled(true); } }; private final IProgressMonitor fNotifyMonitor = new NullProgressMonitor(); public ElementInfoController(final IModelManager manager, final WorkingContext context) { fElementChangeListener = new IElementChangedListener() { @Override public void elementChanged(final ElementChangedEvent event) { ISourceUnit input; IModelElementInputListener[] listeners; synchronized (fInputLock) { if (fNewInput != null && fNewInput.equals(event.delta.getModelElement())) { if (fNewInputJob.getState() != Job.WAITING) { fNewInputJob.schedule(); } return; } if (fInput == null || !fInput.equals(event.delta.getModelElement())) { return; } input = fInput; listeners = fListenerList.toArray(); } try { final IProgressMonitor monitor = new NullProgressMonitor(); Job.getJobManager().beginRule(fNewInputJob, monitor); notifyUpdated(listeners, input, event.delta, monitor); } finally { Job.getJobManager().endRule(fNewInputJob); } } }; fModelProvider = manager; fModelContext = context; fModelProvider.addElementChangedListener(fElementChangeListener, fModelContext); } @Override public void dispose() { fModelProvider.removeElementChangedListener(fElementChangeListener, fModelContext); } public void setInput(final ISourceUnit input) { synchronized (fInputLock) { fInput = null; fNewInput = input; checkNewListeners(); fNewInputJob.cancel(); notifyChanged(fListenerList.toArray(), input); } fNewInputJob.schedule(NEWINPUT_DELAY); } private IModelElementInputListener[] checkNewListeners() { final IModelElementInputListener[] listeners = fNewListenerList.clear(); for (int i = 0; i < listeners.length; i++) { fListenerList.add(listeners[i]); } return listeners; } private void notifyChanged(final IModelElementInputListener[] listeners, final ISourceUnit input) { for (int i = 0; i < listeners.length; i++) { listeners[i].elementChanged(input); } } private void notifyInitial(final IModelElementInputListener[] listeners, final ISourceUnit input, final IProgressMonitor monitor) { if (input != fInput) { return; } try { input.connect(monitor); for (int i = 0; i < listeners.length; i++) { if (input != fInput) { return; } listeners[i].elementInitialInfo(input); } } finally { input.disconnect(monitor); } } private void notifyUpdated(final IModelElementInputListener[] listeners, final ISourceUnit input, final IModelElementDelta delta, final IProgressMonitor monitor) { if (input != fInput) { return; } try { input.connect(monitor); for (int i = 0; i < listeners.length; i++) { if (input != fInput) { return; } listeners[i].elementUpdatedInfo(input, delta); } } finally { input.disconnect(monitor); } } @Override public ISourceUnit getInput() { return fInput; } @Override public void addListener(final IModelElementInputListener listener) { synchronized (fInputLock) { ISourceUnit input = fNewInput; if (input == null) { input = fInput; } if (input != null) { notifyChanged(new IModelElementInputListener[] { listener }, input); } if (input == null || fNewInput == input) { fListenerList.add(listener); return; } fNewListenerList.add(listener); } if (fNewInputJob.getState() != Job.WAITING) { fNewInputJob.schedule(); } } @Override public void removeListener(final IModelElementInputListener listener) { fNewListenerList.remove(listener); fListenerList.remove(listener); } }