/*==========================================================================*\ | $Id: ExtraOptionsUpdater.java,v 1.5 2009/09/13 21:57:15 aallowat Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2009 Virginia Tech | | This file is part of Web-CAT Eclipse Plugins. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU General Public License as published by | the Free Software Foundation; either version 2 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package net.sf.webcat.eclipse.cxxtest.internal.options; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.SortedMap; import java.util.TreeMap; import net.sf.webcat.eclipse.cxxtest.CxxTestPlugin; import net.sf.webcat.eclipse.cxxtest.options.IExtraOptionsUpdater; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo; import org.eclipse.cdt.managedbuilder.core.ITool; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.core.expressions.EvaluationContext; import org.eclipse.core.expressions.EvaluationResult; import org.eclipse.core.expressions.Expression; import org.eclipse.core.expressions.ExpressionConverter; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; import org.osgi.framework.Version; /** * Maintains the table of extra options handlers defined by all plug-ins * currently loaded in Eclipse. * * @author Tony Allevato (Virginia Tech Computer Science) * @author latest changes by: $Author: aallowat $ * @version $Revision: 1.5 $ $Date: 2009/09/13 21:57:15 $ */ public class ExtraOptionsUpdater implements IExtraOptionsUpdater { // === Methods ============================================================ // ------------------------------------------------------------------------ /** * Creates a new instance of the ExtraOptionsRegistry class. */ public ExtraOptionsUpdater() { isWindows = System.getProperty("os.name").toLowerCase().startsWith( //$NON-NLS-1$ "windows "); //$NON-NLS-1$ optionSets = new HashMap<String, SortedMap<Version, IConfigurationElement>>(); loadExtensions(); } // ------------------------------------------------------------------------ public boolean isUpdateNeeded(IProject project) { Properties versionProps = loadVersionProperties(project); // If there are a different number of option handlers loaded than there // are in the properties file for the project, then something is // mismatched and it needs an update. if(versionProps.size() != optionSets.size()) { return true; } for(String loadedId : optionSets.keySet()) { Version latestVersion = getLatestVersion(loadedId); Version projectVersion = Version.parseVersion(versionProps.getProperty(loadedId)); if(projectVersion.compareTo(latestVersion) < 0) { return true; } } return false; } // ------------------------------------------------------------------------ public void updateOptions(IProject project) { Properties versionProps = loadVersionProperties(project); for(String loadedId : optionSets.keySet()) { if(versionProps.containsKey(loadedId)) { Version version = Version.parseVersion( versionProps.getProperty(loadedId)); removeOptions(project, loadedId, version); } addLatestOptions(project, loadedId); Version latestVersion = getLatestVersion(loadedId); versionProps.setProperty(loadedId, latestVersion.toString()); } storeVersionProperties(project, versionProps); } // ------------------------------------------------------------------------ public String[] getLatestCxxTestRunnerIncludes(IConfiguration configuration) { ArrayList<String> includeList = new ArrayList<String>(); for(String loadedId : optionSets.keySet()) { SortedMap<Version, IConfigurationElement> optionsForId = optionSets.get(loadedId); if(optionsForId == null) { break; } Version latestVersion = getLatestVersion(loadedId); if(latestVersion == null) { break; } IConfigurationElement optionSets = optionsForId.get(latestVersion); IConfigurationElement[] optionSetElems = optionSets.getChildren("optionSet"); //$NON-NLS-1$ for (IConfigurationElement optionSet : optionSetElems) { boolean enabled = evaluateOptionSetEnablement( optionSet, configuration); if (enabled) { // Add the runner include paths to the configuration. IConfigurationElement[] includeElems = optionSet.getChildren("runnerIncludes"); //$NON-NLS-1$ for(IConfigurationElement includeElem : includeElems) { IConfigurationElement[] pathElems = includeElem.getChildren("includeFile"); //$NON-NLS-1$ for(IConfigurationElement pathElem : pathElems) { String path = pathElem.getAttribute("path"); //$NON-NLS-1$ includeList.add(path); } } } } } return includeList.toArray(new String[includeList.size()]); } // ------------------------------------------------------------------------ public void removeAllOptions(IProject project) { Properties versionProps = loadVersionProperties(project); for(String loadedId : optionSets.keySet()) { if(versionProps.containsKey(loadedId)) { Version version = Version.parseVersion( versionProps.getProperty(loadedId)); removeOptions(project, loadedId, version); } } versionProps.clear(); storeVersionProperties(project, versionProps); } // ------------------------------------------------------------------------ /** * Gets the latest version number in use by option handlers with a * particular unique identifier. */ private Version getLatestVersion(String id) { if(optionSets.containsKey(id)) return optionSets.get(id).lastKey(); else return null; } // ------------------------------------------------------------------------ private boolean evaluateOptionSetEnablement(IConfigurationElement optionSet, IConfiguration configuration) { IConfigurationElement[] enablementElems = optionSet.getChildren("enablement"); //$NON-NLS-1$ boolean enabled = true; if (enablementElems != null && enablementElems.length > 0) { try { Expression expression = ExpressionConverter.getDefault().perform( enablementElems[0]); EvaluationContext context = new EvaluationContext( null, configuration); EvaluationResult result = expression.evaluate(context); enabled = (result == EvaluationResult.TRUE); } catch (CoreException e) { enabled = false; } } return enabled; } // ------------------------------------------------------------------------ private void addLatestOptions(IProject project, String id) { SortedMap<Version, IConfigurationElement> optionsForId = optionSets.get(id); if(optionsForId == null) { return; } Version latestVersion = getLatestVersion(id); if(latestVersion == null) { return; } IConfigurationElement optionSets = optionsForId.get(latestVersion); IConfigurationElement[] optionSetElems = optionSets.getChildren("optionSet"); //$NON-NLS-1$ IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project); IConfiguration[] configs = buildInfo.getManagedProject().getConfigurations(); for (IConfiguration config : configs) { for (IConfigurationElement optionSet : optionSetElems) { boolean enabled = evaluateOptionSetEnablement( optionSet, config); if (enabled) { // Add the runner include paths to the configuration. IConfigurationElement[] includeElems = optionSet.getChildren("runnerIncludes"); //$NON-NLS-1$ for(IConfigurationElement includeElem : includeElems) { String pluginId = includeElem.getAttribute("pluginId"); //$NON-NLS-1$ String path = includeElem.getAttribute("path"); //$NON-NLS-1$ if(pluginId == null) { pluginId = optionSet.getContributor().getName(); } String includePath = getBundleEntryPath(pluginId, path); try { addCxxTestRunnerInclude(config, includePath); } catch (BuildException e) { e.printStackTrace(); } } IConfigurationElement[] toolElems = optionSet.getChildren("tool"); //$NON-NLS-1$ for(IConfigurationElement toolElem : toolElems) { try { addOptionsForTool(config, toolElem); } catch(BuildException e) { e.printStackTrace(); } } } } } ManagedBuildManager.saveBuildInfo(project, true); } // ------------------------------------------------------------------------ private void addOptionsForTool(IConfiguration config, IConfigurationElement toolElem) throws BuildException { String superClassId = toolElem.getAttribute("superClassId"); //$NON-NLS-1$ ITool[] tools = config.getToolsBySuperClassId(superClassId); IConfigurationElement[] optionElems = toolElem.getChildren(); for(ITool tool : tools) { for(IConfigurationElement optionElem : optionElems) { String optionType = optionElem.getName(); String optionId = optionElem.getAttribute("id"); //$NON-NLS-1$ if(optionType.equals("includesOption")) //$NON-NLS-1$ { String[] newEntries = getPathsForOption(optionElem, true); ProjectOptionsUtil.addToIncludes(tool, optionId, newEntries); } else if(optionType.equals("librariesOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.addToLibraries(tool, optionId, newEntries); } else if(optionType.equals("definedSymbolsOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.addToDefinedSymbols(tool, optionId, newEntries); } else if(optionType.equals("stringListOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.addToStringList(tool, optionId, newEntries); } else if(optionType.equals("pathListOption")) //$NON-NLS-1$ { String[] newEntries = getPathsForOption(optionElem, true); ProjectOptionsUtil.addToStringList(tool, optionId, newEntries); } else if(optionType.equals("splitStringOption")) //$NON-NLS-1$ { String[] newEntries = getItemsAndPathsForOption(optionElem, true); ProjectOptionsUtil.addToString(tool, optionId, newEntries); } else if(optionType.equals("booleanOption")) //$NON-NLS-1$ { Boolean newValue = Boolean.parseBoolean(optionElem.getAttribute("value")); //$NON-NLS-1$ ProjectOptionsUtil.setBoolean(tool, optionId, newValue); } else if(optionType.equals("stringOption")) //$NON-NLS-1$ { } } } } // ------------------------------------------------------------------------ private void addCxxTestRunnerInclude(IConfiguration config, String path) throws BuildException { String superClassId = "cdt.managedbuild.tool.gnu.cpp.compiler"; //$NON-NLS-1$ ITool[] tools = config.getToolsBySuperClassId(superClassId); String optionId = "gnu.cpp.compiler.option.include.paths"; //$NON-NLS-1$ for(ITool tool : tools) { path = "\"" + path.replace('\\', '/') + "\""; //$NON-NLS-1$ //$NON-NLS-2$ String[] newEntries = new String[] { path }; ProjectOptionsUtil.addToIncludes(tool, optionId, newEntries); } } // ------------------------------------------------------------------------ private void removeOptionsForTool(IProject project, IConfiguration config, IConfigurationElement toolElem) throws BuildException { String superClassId = toolElem.getAttribute("superClassId"); //$NON-NLS-1$ ITool[] tools = config.getToolsBySuperClassId(superClassId); IConfigurationElement[] optionElems = toolElem.getChildren(); for(ITool tool : tools) { for(IConfigurationElement optionElem : optionElems) { String optionType = optionElem.getName(); String optionId = optionElem.getAttribute("id"); //$NON-NLS-1$ if(optionType.equals("includesOption")) //$NON-NLS-1$ { String[] newEntries = getPathPatternsForOption(optionElem, true); ProjectOptionsUtil.removeFromIncludesIf(tool, optionId, new RegexOptionPredicate(newEntries)); } else if(optionType.equals("librariesOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.removeFromLibrariesIf(tool, optionId, new ChoiceOptionPredicate(newEntries)); } else if(optionType.equals("definedSymbolsOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.removeFromDefinedSymbolsIf(tool, optionId, new ChoiceOptionPredicate(newEntries)); } else if(optionType.equals("stringListOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.removeFromStringListIf(tool, optionId, new ChoiceOptionPredicate(newEntries)); } else if(optionType.equals("pathListOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.removeFromStringListIf(tool, optionId, new ChoiceOptionPredicate(newEntries)); newEntries = getPathPatternsForOption(optionElem, true); ProjectOptionsUtil.removeFromStringListIf(tool, optionId, new RegexOptionPredicate(newEntries)); } else if(optionType.equals("splitStringOption")) //$NON-NLS-1$ { String[] newEntries = getItemsForOption(optionElem); ProjectOptionsUtil.removeFromStringIf(tool, optionId, new ChoiceOptionPredicate(newEntries)); } else if(optionType.equals("booleanOption")) //$NON-NLS-1$ { Boolean newValue = Boolean.parseBoolean(optionElem.getAttribute("value")); //$NON-NLS-1$ String ignore = optionElem.getAttribute("ignoreOnRemove"); //$NON-NLS-1$ if(ignore != null && !Boolean.parseBoolean(ignore)) ProjectOptionsUtil.setBoolean(tool, optionId, !newValue); } ManagedBuildManager.saveBuildInfo(project, true); } } } // ------------------------------------------------------------------------ private String[] getItemsForOption(IConfigurationElement optionElem) { IConfigurationElement[] itemElems = optionElem.getChildren("item"); //$NON-NLS-1$ ArrayList<String> items = new ArrayList<String>(); for(IConfigurationElement itemElem : itemElems) items.add(itemElem.getAttribute("value")); //$NON-NLS-1$ String[] itemArray = new String[items.size()]; items.toArray(itemArray); return itemArray; } // ------------------------------------------------------------------------ private String[] getItemsAndPathsForOption( IConfigurationElement optionElem, boolean quoted) { IConfigurationElement[] itemElems = optionElem.getChildren(); ArrayList<String> items = new ArrayList<String>(); for(IConfigurationElement itemElem : itemElems) { if(itemElem.getName().equals("item")) //$NON-NLS-1$ { items.add(itemElem.getAttribute("value")); //$NON-NLS-1$ } else if(itemElem.getName().equals("path")) //$NON-NLS-1$ { String fullPath = getPathFromElement(itemElem, quoted); if(fullPath != null) items.add(fullPath); } } String[] itemArray = new String[items.size()]; items.toArray(itemArray); return itemArray; } // ------------------------------------------------------------------------ private String getBundleEntryPath(String pluginId, String relativePath) { String path = null; try { Bundle bundle = Platform.getBundle(pluginId); if(bundle == null) return null; URL entryURL = FileLocator.find(bundle, new Path(relativePath), null); URL url = FileLocator.resolve(entryURL); path = url.getFile(); // This special check is somewhat shady, but it looks like it's // the only way to handle a Windows path properly, since Eclipse // returns a string like "/C:/folder/...". The Cygwin make tools // don't like paths with colons in them, so we convert them to // the "/cygdrive/c/..." format instead. if(isWindows && path.charAt(2) == ':') { char letter = Character.toLowerCase(path.charAt(1)); path = "/cygdrive/" + letter + path.substring(3); //$NON-NLS-1$ } path = new Path(path).toOSString(); if(path.charAt(path.length() - 1) == File.separatorChar) path = path.substring(0, path.length() - 1); } catch(IOException e) { e.printStackTrace(); } return path; } // ------------------------------------------------------------------------ private String getPathFromElement(IConfigurationElement pathElem, boolean quoted) { String prefix = pathElem.getAttribute("prefix"); //$NON-NLS-1$ if(prefix == null) prefix = ""; //$NON-NLS-1$ String pluginId = pathElem.getAttribute("pluginId"); //$NON-NLS-1$ String relPath = pathElem.getAttribute("relativePath"); //$NON-NLS-1$ String fullPath; if(pluginId == null) fullPath = relPath; else fullPath = getBundleEntryPath(pluginId, relPath); if(fullPath != null) { fullPath = fullPath.replace('\\', '/'); if(quoted) return (prefix + "\"" + fullPath + "\""); //$NON-NLS-1$ //$NON-NLS-2$ else return (prefix + fullPath); } return null; } // ------------------------------------------------------------------------ private String[] getPathsForOption(IConfigurationElement optionElem, boolean quoted) { IConfigurationElement[] pathElems = optionElem.getChildren("path"); //$NON-NLS-1$ ArrayList<String> paths = new ArrayList<String>(); for(IConfigurationElement pathElem : pathElems) { String fullPath = getPathFromElement(pathElem, quoted); if(fullPath != null) paths.add(fullPath); } String[] pathArray = new String[paths.size()]; paths.toArray(pathArray); return pathArray; } // ------------------------------------------------------------------------ private String[] getPathPatternsForOption(IConfigurationElement optionElem, boolean quoted) { IConfigurationElement[] pathElems = optionElem.getChildren("path"); //$NON-NLS-1$ ArrayList<String> patterns = new ArrayList<String>(); for(IConfigurationElement pathElem : pathElems) { String fullPath = getPathFromElement(pathElem, quoted); if(fullPath != null) { boolean isDir = new File(fullPath).isDirectory(); String pattern = ShellStringUtils.patternForAnyVersionOfPluginRelativePath( fullPath, isDir); patterns.add(pattern); } } String[] patternArray = new String[patterns.size()]; patterns.toArray(patternArray); return patternArray; } // ------------------------------------------------------------------------ private void removeOptions(IProject project, String id, Version version) { SortedMap<Version, IConfigurationElement> optionsForId = optionSets.get(id); if(optionsForId == null) { return; } Version latestVersion = getLatestVersion(id); if(latestVersion == null) { return; } IConfigurationElement optionSets = optionsForId.get(latestVersion); IConfigurationElement[] optionSetElems = optionSets.getChildren("optionSet"); //$NON-NLS-1$ IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project); IConfiguration[] configs = buildInfo.getManagedProject().getConfigurations(); for (IConfiguration config : configs) { for (IConfigurationElement optionSet : optionSetElems) { // TODO this needs to be fixed; we need to remember the // preferences that were previously enabled and use THAT // information to remove the correct options, somehow boolean enabled = true; //evaluateOptionSetEnablement( //optionSet, config); if (enabled) { IConfigurationElement[] toolElems = optionSet.getChildren("tool"); //$NON-NLS-1$ for(IConfigurationElement toolElem : toolElems) { try { removeOptionsForTool(project, config, toolElem); } catch(BuildException e) { e.printStackTrace(); } } } } } ManagedBuildManager.saveBuildInfo(project, true); } // ------------------------------------------------------------------------ /** * Loads the extra options handlers from all currently loaded plug-ins. */ private void loadExtensions() { IExtensionRegistry registry = Platform.getExtensionRegistry(); IExtensionPoint extensionPoint = registry.getExtensionPoint(CxxTestPlugin.PLUGIN_ID + ".extraProjectOptions"); //$NON-NLS-1$ IConfigurationElement[] elements = extensionPoint.getConfigurationElements(); for(IConfigurationElement element : elements) loadExtraProjectOptions(element); } // ------------------------------------------------------------------------ /** * A helper function to populate the option handler table. */ private void loadExtraProjectOptions(IConfigurationElement element) { String optionsId = element.getAttribute("id"); //$NON-NLS-1$ IConfigurationElement[] optionSetsElems = element.getChildren("optionSets"); //$NON-NLS-1$ SortedMap<Version, IConfigurationElement> optionSetsForVersion = new TreeMap<Version, IConfigurationElement>(); for(IConfigurationElement optionSetsElem : optionSetsElems) { Version version = new Version(optionSetsElem.getAttribute("version")); //$NON-NLS-1$ optionSetsForVersion.put(version, optionSetsElem); } if(optionSetsForVersion.size() != 0) { optionSets.put(optionsId, optionSetsForVersion); } } // ------------------------------------------------------------------------ private Properties loadVersionProperties(IProject project) { Properties properties = new Properties(); try { IFile propIFile = project.getFile(PROPERTIES_FILE); File propFile = propIFile.getRawLocation().toFile(); if(propFile.exists()) properties.load(new FileInputStream(propFile)); } catch(IOException e) { e.printStackTrace(); } return properties; } // ------------------------------------------------------------------------ private void storeVersionProperties(IProject project, Properties properties) { try { IFile propIFile = project.getFile(PROPERTIES_FILE); File propFile = propIFile.getRawLocation().toFile(); properties.store(new FileOutputStream(propFile), PROPERTIES_COMMENT); } catch(IOException e) { e.printStackTrace(); } } // === Static Variables =================================================== /** * The name of the properties file that keeps track of the version of each * option handler's settings. */ private static final String PROPERTIES_FILE = ".cxxtest.versions.properties"; //$NON-NLS-1$ /** * A comment added to the top of the option version properties file. */ private static final String PROPERTIES_COMMENT = "Automatically generated file -- DO NOT MODIFY"; //$NON-NLS-1$ // === Instance Variables ================================================= /** * A table that holds all of the extra options handlers registered in loaded * plug-ins, keyed by their unique identifier. For each version key, the * value of that key is a sorted map that contains all of the * IExtraProjectOptions instances available for that option set, keyed by * their version number. */ private Map<String, SortedMap<Version, IConfigurationElement>> optionSets; private boolean isWindows; }