/******************************************************************************* * Copyright (c) 2015 ARM Ltd. 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 Ltd and ARM Germany GmbH - Initial API and implementation * * Resource change listener snippet is taken from: * https://www.eclipse.org/articles/Article-Resource-deltas/resource-deltas.html * *******************************************************************************/ package com.arm.cmsis.pack.project; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.IExecutionListener; import org.eclipse.core.commands.NotHandledException; 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.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.commands.ICommandService; import com.arm.cmsis.pack.CpPlugIn; import com.arm.cmsis.pack.common.CmsisConstants; import com.arm.cmsis.pack.configuration.IRteConfiguration; import com.arm.cmsis.pack.data.ICpPack; import com.arm.cmsis.pack.data.ICpPackCollection; import com.arm.cmsis.pack.events.RteEvent; import com.arm.cmsis.pack.events.RteEventProxy; import com.arm.cmsis.pack.ui.CpPlugInUI; /** * Class that manages RTE projects and their associations to ICproject and IProject */ public class RteProjectManager extends RteEventProxy implements IResourceChangeListener, IExecutionListener { private RteSetupParticipant rteSetupParticipant = null; private Map<String, IRteProject> rteProjects = Collections.synchronizedMap(new HashMap<>()); private boolean executionListenerRegistered = false; boolean postponeRefresh = false; /** * Default constructor */ public RteProjectManager() { IWorkspace workspace = ResourcesPlugin.getWorkspace(); workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); CpPlugIn.addRteListener(this); } /** * Clears internal collection of the projects */ public void destroy() { IWorkspace workspace = ResourcesPlugin.getWorkspace(); workspace.removeResourceChangeListener(this); CpPlugIn.removeRteListener(this); if(executionListenerRegistered) { ICommandService commandService = CpPlugInUI.getCommandService(); if(commandService != null) { commandService.removeExecutionListener(this); } } synchronized (rteProjects) { // do it as atomic operation for(IRteProject rteProject : rteProjects.values()) { rteProject.destroy(); } rteProjects.clear(); } } private void registerExecutionListener() { if(executionListenerRegistered) { return; } ICommandService commandService = CpPlugInUI.getCommandService(); if(commandService != null) { commandService.addExecutionListener(this); executionListenerRegistered = true; } } /** * Initializes RteSetupParticipant does nothing if already initialized */ public void initRteSetupParticipant() { if(rteSetupParticipant == null) { rteSetupParticipant = new RteSetupParticipant(); } } /** * Triggers project index update and notifies that project is updated * @param project IProject associated with an RTE project */ public void updateIndex(IProject project) { if(rteSetupParticipant != null) { rteSetupParticipant.updateIndex(project); } emitRteEvent(RteEvent.PROJECT_UPDATED, getRteProject(project)); } /** * Returns IRteProject associated for given name * @param project IProject object associated with IRteProject * @return IRteProject */ synchronized public IRteProject getRteProject(String name) { return rteProjects.get(name); } /** * Returns IRteProject associated with given IRteProject if any * @param project IProject object associated with IRteProject * @return IRteProject */ public IRteProject getRteProject(IProject project) { if(project != null) { return getRteProject(project.getName()); } return null; } public Collection<IRteProject> getRteProjects() { return rteProjects.values(); } /** * Creates or returns existing IRteProject associated with given IRteProject * @param project IProject object to be associated with IRteProject * @return existing IRteProject if exists or new one */ synchronized public IRteProject createRteProject(IProject project) { IRteProject rteProject = getRteProject(project); if(rteProject == null) { rteProject = new RteProject(project); addRteProject(rteProject); registerExecutionListener(); // ensure refresh action is attached } return rteProject; } /** * Adds RTE project to the internal collection * @param rteProject IRteProject to add */ synchronized public void addRteProject(IRteProject rteProject) { if(rteProject != null) { rteProjects.put(rteProject.getName(), rteProject); emitRteEvent(RteEvent.PROJECT_ADDED, rteProject); } } /** * Removes RTE project from internal collection * @param rteProject IRteProject to remove */ synchronized public void deleteRteProject(IRteProject rteProject) { if(rteProject != null) { rteProjects.remove(rteProject.getName()); rteProject.destroy(); emitRteEvent(RteEvent.PROJECT_REMOVED, rteProject); } } /** * Renames RTE project and updates collection * @param rteProject IRteProject to remove */ public void renameRteProject(String oldName, String newName) { IRteProject rteProject = getRteProject(oldName); if(rteProject != null) { synchronized(rteProjects) { // do it as atomic operation rteProject.setName(newName); rteProjects.remove(oldName); rteProjects.put(newName, rteProject); emitRteEvent(RteEvent.PROJECT_UPDATED, rteProject); } } } @Override public void handle(RteEvent event) { switch (event.getTopic()) { case RteEvent.PACKS_RELOADED: case RteEvent.PACKS_UPDATED: refreshProjects(); break; case RteEvent.GPDSC_CHANGED: refreshGpdscProjects((String) event.getData()); break; case RteEvent.PRE_IMPORT: postponeRefresh = true; break; case RteEvent.POST_IMPORT: postponeRefresh = false; IProject project = (IProject) event.getData(); if (project != null) { IRteProject rteProject = getRteProject(project); if (rteProject != null) { rteProject.refresh(); } } default: return; } } protected void refreshProjects() { synchronized(rteProjects) { for(IRteProject rteProject : rteProjects.values()) { if (rteProject.getProject().isOpen()) { rteProject.refresh(); } } } } protected void refreshGpdscProjects(String file) { synchronized(rteProjects) { for(IRteProject rteProject : rteProjects.values()) { if (rteProject.getProject().isOpen()) { IRteConfiguration rteConf = rteProject.getRteConfiguration(); if(rteConf != null && rteConf.isGeneratedPackUsed(file)) { rteProject.refresh(); } } } } } @Override public void resourceChanged(IResourceChangeEvent event) { // consider only POST_CHANGE events if (event.getType() != IResourceChangeEvent.POST_CHANGE) { return; } IResourceDelta resourseDelta = event.getDelta(); IResourceDeltaVisitor deltaVisitor = new IResourceDeltaVisitor() { @Override public boolean visit(IResourceDelta delta) { IResource resource = delta.getResource(); int type = resource.getType(); if (type == IResource.ROOT) { return true; // workspace => visit children } IProject project = resource.getProject(); int kind = delta.getKind(); int flags = delta.getFlags(); if (type == IResource.PROJECT && kind == IResourceDelta.REMOVED) { IRteProject rteProject = getRteProject(project); if (rteProject == null) { return false; // not an RTE project or not loaded => ignore } if ((flags & IResourceDelta.MOVED_TO) == IResourceDelta.MOVED_TO) { // renamed IPath newPath = delta.getMovedToPath(); if(newPath == null) { return false; } String newName = newPath.lastSegment(); renameRteProject(project.getName(), newName); return false; } // removed deleteRteProject(rteProject); return false; } // only consider RTE projects if (!RteProjectNature.hasRteNature(project)) { return false; // skip children } if (type == IResource.PROJECT) { // is project renamed? if (kind == IResourceDelta.REMOVED) { if ((flags & IResourceDelta.CHANGED) != 0) { return false; } else if ((flags & IResourceDelta.MOVED_TO) != 0) { return false; } return true; } } else if (type == IResource.FILE) { // is resource changed? if (kind != IResourceDelta.CHANGED) { return true; } // is content changed? if ((flags & IResourceDelta.CONTENT) == 0) { return true; } // check only RTE configuration files with ".rteconfig" extension if (CmsisConstants.RTECONFIG.equalsIgnoreCase(resource.getFileExtension())) { IRteProject rteProject = getRteProject(project); if (rteProject != null) { String relName = resource.getProjectRelativePath().toString(); if (!postponeRefresh && relName.equals(rteProject.getRteConfigurationName())) { rteProject.refresh(); } } return false; } } return true; } }; try { resourseDelta.accept(deltaVisitor); } catch (CoreException e) { e.printStackTrace(); } } @Override public void preExecute(String commandId, ExecutionEvent event) { if (!org.eclipse.ui.IWorkbenchCommandConstants.FILE_REFRESH.equals(commandId)) { return; } ISelectionService selService = CpPlugInUI.getSelectionService(); if(selService == null) { return; } // refresh RTE project in the selection ISelection selection = selService.getSelection(); Collection<IProject> projects = CpPlugInUI.getProjectsFromSelection(selection); if(projects == null || projects.isEmpty()) { return; } for(IProject project : projects) { IRteProject rteProject = getRteProject(project); if(rteProject != null && rteProject.isUpdateCompleted()) { rteProject.refresh(); } } } synchronized public void updateProject(IRteProject rteProject, int updateFlags) { if(rteProject == null) { return; } IProject project = rteProject.getProject(); if(project == null || !project.isOpen()) { return; } rteProject.setUpdateCompleted(false); RteProjectUpdater updater = new RteProjectUpdater(rteProject, updateFlags); updater.schedule(); } /** * Check if the pack manager contains the pack and it is already installed * @param packAttributes pack attributes * @return true if the pack installer has already installed the pack */ protected boolean isInstalled(String packId) { ICpPackCollection installedPacks = CpPlugIn.getPackManager().getInstalledPacks(); if (installedPacks == null) { return false; } ICpPack pack = installedPacks.getPack(packId); return pack != null; } @Override public void notHandled(String commandId, NotHandledException exception) { // does nothing } @Override public void postExecuteFailure(String commandId, ExecutionException exception) { // does nothing } @Override public void postExecuteSuccess(String commandId, Object returnValue) { // does nothing } }