/******************************************************************************* * Copyright © 2012, 2013 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.edt.ide.core.internal.builder; import java.util.HashMap; import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.edt.compiler.binding.ITypeBinding; import org.eclipse.edt.compiler.internal.io.IRFileNameUtility; import org.eclipse.edt.compiler.tools.EGL2IR; import org.eclipse.edt.ide.core.CoreIDEPluginStrings; import org.eclipse.edt.ide.core.EDTCoreIDEPlugin; import org.eclipse.edt.ide.core.EDTCorePreferenceConstants; import org.eclipse.edt.ide.core.internal.lookup.FileInfoManager; import org.eclipse.edt.ide.core.internal.lookup.IFileInfo; import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPathManager; import org.eclipse.edt.ide.core.model.EGLCore; import org.eclipse.edt.ide.core.model.EGLModelException; import org.eclipse.edt.ide.core.model.IPackageFragmentRoot; import org.eclipse.edt.ide.core.utils.ProjectSettingsUtility; import org.eclipse.edt.mof.serialization.ZipFileObjectStore; import org.osgi.service.prefs.Preferences; /** * Manages listeners for project-specific settings. */ public class ProjectSettingsListenerManager { private static final ProjectSettingsListenerManager instance = new ProjectSettingsListenerManager(); public static final Object FAMILY_PROCESS_GENERATOR_CHANGES = new Object(); private Map<IProject, IPreferenceChangeListener> listeners; private ProjectSettingsListenerManager() { listeners = new HashMap<IProject, IPreferenceChangeListener>(); // Initialize the currently open projects. IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); for (IProject project : projects) { if (project.isAccessible()) { addProject(project); } } } public static ProjectSettingsListenerManager getInstance() { return instance; } public void addProject(IProject project) { if (!listeners.containsKey(project)) { GeneratorPreferenceListener listener = new GeneratorPreferenceListener(project); Preferences prefs = new ProjectScope(project).getNode(EDTCoreIDEPlugin.PLUGIN_ID).node(ProjectSettingsUtility.PROPERTY_GENERATOR_IDS); if (prefs instanceof IEclipsePreferences) { ((IEclipsePreferences)prefs).addPreferenceChangeListener(listener); } prefs = new ProjectScope(project).getNode(EDTCoreIDEPlugin.PLUGIN_ID).node(EDTCorePreferenceConstants.BUILD_FLAG); if (prefs instanceof IEclipsePreferences) { ((IEclipsePreferences)prefs).addPreferenceChangeListener(listener); } listeners.put(project, listener); } } public void removeProject(IProject project) { IPreferenceChangeListener listener = listeners.remove(project); if (listener != null) { Preferences prefs = new ProjectScope(project).getNode(EDTCoreIDEPlugin.PLUGIN_ID).node(ProjectSettingsUtility.PROPERTY_GENERATOR_IDS); if (prefs instanceof IEclipsePreferences) { ((IEclipsePreferences)prefs).removePreferenceChangeListener(listener); } prefs = new ProjectScope(project).getNode(EDTCoreIDEPlugin.PLUGIN_ID).node(EDTCorePreferenceConstants.BUILD_FLAG); if (prefs instanceof IEclipsePreferences) { ((IEclipsePreferences)prefs).removePreferenceChangeListener(listener); } } } /** * When the generator(s) for a resource are modified, this causes all affected resources to be regenerated. */ private class GeneratorPreferenceListener implements IPreferenceChangeListener { final IProject project; final Preferences prefs; public GeneratorPreferenceListener(IProject project) { this.project = project; this.prefs = new ProjectScope(project).getNode(EDTCoreIDEPlugin.PLUGIN_ID).node(ProjectSettingsUtility.PROPERTY_GENERATOR_IDS); } @Override public void preferenceChange(PreferenceChangeEvent event) { // Regenerate the entire resource tree that's affected. final IResource source; String key = event.getKey(); if (ProjectSettingsUtility.PROJECT_KEY.equals(key)) { source = project; } else { source = project.findMember(key); } if (source != null) { final IResource[] sourceDirs; final IResource[] toTraverse; try { IPackageFragmentRoot[] roots = EGLCore.create(project).getPackageFragmentRoots(); sourceDirs = new IResource[roots.length]; for (int i = 0; i < roots.length; i++) { if (roots[i].isArchive()) { continue; } sourceDirs[i] = roots[i].getResource(); } } catch (EGLModelException e) { EDTCoreIDEPlugin.log(e); return; } // For projects we just want to traverse the source folders if (source.getType() == IResource.PROJECT) { toTraverse = sourceDirs; } else { toTraverse = new IResource[]{source}; } WorkspaceJob job = new WorkspaceJob(CoreIDEPluginStrings.calculatingGeneratorChanges) { public IStatus runInWorkspace(final IProgressMonitor monitor) throws CoreException { final IContainer outputFolder = ProjectBuildPathManager.getInstance().getProjectBuildPath(project).getOutputLocation(); for (final IResource nextResource : toTraverse) { if (nextResource == null) { // Eglar entry continue; } // We're checking the .egl files, but we need to be able to strip off the package fragment root from its path. // This will allow us to find the IR in the output directory. IPath sourceDir = null; for (IResource nextSrcDir : sourceDirs) { if (nextSrcDir == null) { continue; } if (nextSrcDir.getFullPath().isPrefixOf(nextResource.getFullPath())) { sourceDir = nextSrcDir.getFullPath(); break; } } if (sourceDir == null) { continue; } final int sourceDirSegments = sourceDir.segmentCount(); nextResource.accept(new IResourceProxyVisitor() { @Override public boolean visit(IResourceProxy proxy) throws CoreException { IResource resource = proxy.requestResource(); // If it's the originating resource, or its a child resource that inherits settings from the originating resource, // then it should be regenerated if a file, otherwise check its kids. try { if (resource.equals(source) || ProjectSettingsUtility.findSetting(resource.getFullPath(), prefs, false) == null) { if (resource.getType() == IResource.FILE) { IFileInfo info = FileInfoManager.getInstance().getFileInfo( project, resource.getProjectRelativePath() ); if (info != null) { IPath relativeFolderPath = resource.getFullPath().removeFirstSegments(sourceDirSegments).removeLastSegments( 1 ); for (Object name : info.getPartNames()) { if (info.getPartType((String)name) == ITypeBinding.FILE_BINDING) { continue; } // We really don't know which extension the IR will have, so check for all known extensions. String relativePath = IRFileNameUtility.toIRFileName(relativeFolderPath.append((String)name).toString()); IResource file = outputFolder.findMember(relativePath + EGL2IR.EGLXML); if (file != null) { file.touch(null); } file = outputFolder.findMember(relativePath + EGL2IR.EGLBIN); if (file != null) { file.touch(null); } file = outputFolder.findMember(relativePath + ZipFileObjectStore.MOFXML); if (file != null) { file.touch(null); } file = outputFolder.findMember(relativePath + ZipFileObjectStore.MOFBIN); if (file != null) { file.touch(null); } } } return false; // files don't have kids } return true; } } catch (IllegalStateException e) { // Happens when blowing over a project with a newer copy, e.g. check out project from CVS // to replace the version in the workspace. Not much we can do here except to NOT regenerate. // That's okay though, a generate will happen after the project is automatically recompiled. } return false; } }, 0); } return Status.OK_STATUS; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#belongsTo(Object) */ public boolean belongsTo(Object family) { return family == FAMILY_PROCESS_GENERATOR_CHANGES; } }; job.schedule(); } } } }