/** * Copyright (c) 2011 committers of YAKINDU 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: * committers of YAKINDU - initial API and implementation * */ package org.yakindu.sct.simulation.core.hmr; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchListener; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.workspace.util.WorkspaceSynchronizer; import org.yakindu.sct.model.sgraph.Statechart; import org.yakindu.sct.simulation.core.debugmodel.SCTDebugElement; import org.yakindu.sct.simulation.core.debugmodel.SCTDebugTarget; import org.yakindu.sct.simulation.core.util.ResourceUtil; /** * * @author andreas muelder - Initial contribution and API * */ public class SCTHotModelReplacementManager implements IResourceChangeListener, IResourceDeltaVisitor, ILaunchListener, IDebugEventSetListener { public static final SCTHotModelReplacementManager INSTANCE = new SCTHotModelReplacementManager(); private List<IDebugTarget> activeTargets; private List<IHotModelReplacementListener> listeners; private SCTHotModelReplacementManager() { activeTargets = new ArrayList<IDebugTarget>(); listeners = new ArrayList<IHotModelReplacementListener>(); } public synchronized void addReplacementListener(IHotModelReplacementListener listener) { listeners.add(listener); } public synchronized void removeReplacementListener(IHotModelReplacementListener listener) { listeners.remove(listener); } public void startup() { DebugPlugin.getDefault().addDebugEventListener(this); DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); } public void tearDown() { DebugPlugin.getDefault().removeDebugEventListener(this); DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this); } public void handleDebugEvents(DebugEvent[] events) { for (DebugEvent debugEvent : events) { if (debugEvent.getKind() == DebugEvent.TERMINATE) { Object source = debugEvent.getSource(); if (source instanceof IAdaptable) { Object adapter = ((IAdaptable) source).getAdapter(IDebugTarget.class); if (adapter instanceof SCTDebugTarget) { unregisterSCTTarget((SCTDebugTarget) adapter); } } } } } public void launchRemoved(ILaunch launch) { IDebugTarget[] debugTargets = launch.getDebugTargets(); for (IDebugTarget debugTarget : debugTargets) { if (debugTarget instanceof SCTDebugTarget) { unregisterSCTTarget((SCTDebugTarget) debugTarget); } } } public void launchAdded(ILaunch launch) { IDebugTarget[] debugTargets = launch.getDebugTargets(); for (IDebugTarget debugTarget : debugTargets) { if (debugTarget instanceof SCTDebugTarget) { registerSCTTarget((SCTDebugTarget) debugTarget); } } } protected void registerSCTTarget(SCTDebugTarget target) { synchronized (this) { // start listening to resource changes if an SCtDebugTarget is // active if (activeTargets.isEmpty()) { ResourcesPlugin.getWorkspace().addResourceChangeListener(this); } if (!activeTargets.contains(target)) activeTargets.add(target); } } protected void unregisterSCTTarget(SCTDebugTarget target) { synchronized (this) { if (activeTargets.contains(target)) { activeTargets.remove((SCTDebugTarget) target); } // Stop listening to resource changes if no SCTDebugTarget is active if (activeTargets.isEmpty()) { ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } } } public void launchChanged(ILaunch launch) { launchAdded(launch); } private List<IFile> changedFiles = new ArrayList<IFile>(); public synchronized void resourceChanged(IResourceChangeEvent event) { if (event.getType() == IResourceChangeEvent.PRE_CLOSE) handleCloseEvent(event); IResourceDelta delta = event.getDelta(); try { changedFiles.clear(); if (delta != null) delta.accept(this); if (changedFiles.size() > 0) { handleHotModelReplacement(); } } catch (CoreException e) { e.printStackTrace(); } } private void handleCloseEvent(IResourceChangeEvent event) { if (event.getResource() instanceof IProject) { IProject project = ((IProject) event.getResource()); for (IDebugTarget target : activeTargets) { EObject object = (EObject) target.getAdapter(EObject.class); IFile file = WorkspaceSynchronizer.getFile(object.eResource()); if (project.equals(file.getProject())) { try { target.terminate(); } catch (DebugException e) { e.printStackTrace(); } } } } } private void handleHotModelReplacement() { // first implementation: If the underlying model does not change // semantically, no notification is required... List<IDebugTarget> targets = getAffectedTargets(); List<IDebugTarget> modelReplacementFailedTargets = new ArrayList<IDebugTarget>(); for (IDebugTarget sctDebugTarget : targets) { // Reload the Statechart form the changes resource Statechart newStatechart = ResourceUtil.loadStatechart(((SCTDebugElement) sctDebugTarget) .getResourceString()); if (!EcoreUtil.equals(newStatechart, (EObject) sctDebugTarget.getAdapter(EObject.class))) { // The model semantically changed, we have to create a // notificiation for that.... modelReplacementFailedTargets.add(sctDebugTarget); } } if (modelReplacementFailedTargets.size() > 0) { notifyHotModelReplacementFailed(targets); } } protected void notifyHotModelReplacementFailed(List<IDebugTarget> affectedTargets) { synchronized (listeners) { for (IHotModelReplacementListener listener : listeners) { listener.hotCodeReplaceFailed(affectedTargets); } } } private List<IDebugTarget> getAffectedTargets() { List<IDebugTarget> targets = new ArrayList<IDebugTarget>(); synchronized (activeTargets) { for (IDebugTarget debugTarget : activeTargets) { if (debugTarget instanceof SCTDebugTarget) { String resourceString = ((SCTDebugElement) debugTarget).getResourceString(); IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(resourceString); if (changedFiles.contains(resource)) { targets.add(debugTarget); } } } } return targets; } public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); if ((delta.getFlags() & IResourceDelta.CONTENT) != 0) { if (resource.getType() == IResource.FILE) { IFile file = (IFile) resource; changedFiles.add(file); } } return true; } }