/******************************************************************************* * Copyright (c) 2003, 2011 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 - Initial API and implementation * Miwako Tokugawa (Intel Corporation) - bug 222817 (OptionCategoryApplicability) *******************************************************************************/ package org.eclipse.cdt.managedbuilder.internal.core; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.internal.core.SafeStringInterner; import org.eclipse.cdt.managedbuilder.core.IBuildObject; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IHoldsOptions; import org.eclipse.cdt.managedbuilder.core.IManagedConfigElement; import org.eclipse.cdt.managedbuilder.core.IOption; import org.eclipse.cdt.managedbuilder.core.IOptionCategory; import org.eclipse.cdt.managedbuilder.core.IOptionCategoryApplicability; import org.eclipse.cdt.managedbuilder.core.IResourceConfiguration; import org.eclipse.cdt.managedbuilder.core.IResourceInfo; import org.eclipse.cdt.managedbuilder.core.ITool; import org.eclipse.cdt.managedbuilder.core.IToolChain; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; import org.eclipse.cdt.managedbuilder.internal.enablement.OptionEnablementExpression; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Path; import org.osgi.framework.Version; /** * */ public class OptionCategory extends BuildObject implements IOptionCategory { private static final String EMPTY_STRING = new String(); private static final IOptionCategory[] emtpyCategories = new IOptionCategory[0]; // Parent and children private IHoldsOptions holder; private List<OptionCategory> children; // Note: These are logical Option Category children, not "model" children // Managed Build model attributes private IOptionCategory owner; // The logical Option Category parent private String ownerId; private URL iconPathURL; private IConfigurationElement applicabilityCalculatorElement = null; private IOptionCategoryApplicability applicabilityCalculator = null; private BooleanExpressionApplicabilityCalculator booleanExpressionCalculator = null; public static final String APPLICABILITY_CALCULATOR = "applicabilityCalculator"; //$NON-NLS-1$ // Miscellaneous private boolean isExtensionOptionCategory = false; private boolean isDirty = false; private boolean resolved = true; /* * C O N S T R U C T O R S */ public OptionCategory(IOptionCategory owner) { this.owner = owner; } /** * This constructor is called to create an option category defined by an extension point in * a plugin manifest file, or returned by a dynamic element provider * * @param parent The IHoldsOptions parent of this category, or <code>null</code> if * defined at the top level * @param element The category definition from the manifest file or a dynamic element * provider */ public OptionCategory(IHoldsOptions parent, IManagedConfigElement element) { this.holder = parent; isExtensionOptionCategory = true; // setup for resolving resolved = false; loadFromManifest(element); // Hook me up to the Managed Build Manager ManagedBuildManager.addExtensionOptionCategory(this); // Add the category to the parent parent.addOptionCategory(this); } /** * Create an <codeOptionCategory</code> based on the specification stored in the * project file (.cdtbuild). * * @param parent The <code>IHoldsOptions</code> object the OptionCategory will be added to. * @param element The XML element that contains the OptionCategory settings. */ public OptionCategory(IHoldsOptions parent, ICStorageElement element) { this.holder = parent; isExtensionOptionCategory = false; // Initialize from the XML attributes loadFromProject(element); // Add the category to the parent parent.addOptionCategory(this); } /* * E L E M E N T A T T R I B U T E R E A D E R S A N D W R I T E R S */ public void loadFromManifest(IManagedConfigElement element) { ManagedBuildManager.putConfigElement(this, element); // id setId(SafeStringInterner.safeIntern(element.getAttribute(IOptionCategory.ID))); // name setName(SafeStringInterner.safeIntern(element.getAttribute(IOptionCategory.NAME))); // owner ownerId = SafeStringInterner.safeIntern(element.getAttribute(IOptionCategory.OWNER)); // icon if ( element.getAttribute(IOptionCategory.ICON) != null && element instanceof DefaultManagedConfigElement) { String icon = element.getAttribute(IOptionCategory.ICON); iconPathURL = ManagedBuildManager.getURLInBuildDefinitions( (DefaultManagedConfigElement)element, new Path(icon) ); } //get enablements IManagedConfigElement enablements[] = element.getChildren(OptionEnablementExpression.NAME); if(enablements.length > 0) booleanExpressionCalculator = new BooleanExpressionApplicabilityCalculator(enablements); // get the applicability calculator, if any String applicabilityCalculatorStr = element.getAttribute(APPLICABILITY_CALCULATOR); if (applicabilityCalculatorStr != null && element instanceof DefaultManagedConfigElement) { applicabilityCalculatorElement = ((DefaultManagedConfigElement)element).getConfigurationElement(); } else { applicabilityCalculator = booleanExpressionCalculator; } } /* (non-Javadoc) * Initialize the OptionCategory information from the XML element * specified in the argument * * @param element An XML element containing the OptionCategory information */ protected void loadFromProject(ICStorageElement element) { // id (unique, do not intern) setId(element.getAttribute(IBuildObject.ID)); // name if (element.getAttribute(IBuildObject.NAME) != null) { setName(SafeStringInterner.safeIntern(element.getAttribute(IBuildObject.NAME))); } // owner if (element.getAttribute(IOptionCategory.OWNER) != null) { ownerId = SafeStringInterner.safeIntern(element.getAttribute(IOptionCategory.OWNER)); } if (ownerId != null) { owner = holder.getOptionCategory(ownerId); } else { owner = getNullOptionCategory(); } // icon - was saved as URL in string form if (element.getAttribute(IOptionCategory.ICON) != null) { String iconPath = element.getAttribute(IOptionCategory.ICON); try { iconPathURL = new URL(iconPath); } catch (MalformedURLException e) { // Print a warning ManagedBuildManager.outputIconError(iconPath); iconPathURL = null; } } // Hook me in if (owner == null) ((HoldsOptions)holder).addChildCategory(this); else if (owner instanceof Tool) ((Tool)owner).addChildCategory(this); else ((OptionCategory)owner).addChildCategory(this); } private IOptionCategory getNullOptionCategory() { // Handle difference between Tool and others by using // the fact that Tool implements IOptionCategory. If so, // the holder is in fact a parent category to this category. if (holder instanceof IOptionCategory) { return (IOptionCategory)holder; } return null; } /** * Persist the OptionCategory to the project file. */ public void serialize(ICStorageElement element) { element.setAttribute(IBuildObject.ID, id); if (name != null) { element.setAttribute(IBuildObject.NAME, name); } if (owner != null) element.setAttribute(IOptionCategory.OWNER, owner.getId()); if (iconPathURL != null) { // Save as URL in string form element.setAttribute(IOptionCategory.ICON, iconPathURL.toString()); } // I am clean now isDirty = false; } /* * P A R E N T A N D C H I L D H A N D L I N G */ /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getChildCategories() */ public IOptionCategory[] getChildCategories() { if (children != null) return children.toArray(new IOptionCategory[children.size()]); else return emtpyCategories; } public void addChildCategory(OptionCategory category) { if (children == null) children = new ArrayList<OptionCategory>(); children.add(category); } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions() */ public Object[][] getOptions(IConfiguration configuration, IHoldsOptions optionHolder) { IHoldsOptions[] optionHolders = new IHoldsOptions[1]; optionHolders[0] = optionHolder; return getOptions(optionHolders, FILTER_PROJECT); } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions() */ public Object[][] getOptions(IConfiguration configuration) { IHoldsOptions[] optionHolders = null; if (configuration != null) { IHoldsOptions optionHolder = getOptionHolder(); if (optionHolder instanceof ITool) { optionHolders = configuration.getFilteredTools(); } else if (optionHolder instanceof IToolChain) { // Get the toolchain of this configuration, which is // the holder equivalent for this option optionHolders = new IHoldsOptions[1]; optionHolders[0] = configuration.getToolChain(); } // TODO: if further option holders were to be added in future, // this function needs to be extended } return getOptions(optionHolders, FILTER_PROJECT); } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions() */ public Object[][] getOptions(IResourceInfo resinfo, IHoldsOptions optionHolder) { IHoldsOptions[] optionHolders = new IHoldsOptions[1]; optionHolders[0] = optionHolder; boolean isRoot = false; if (resinfo instanceof ResourceInfo) isRoot = ((ResourceInfo)resinfo).isRoot(); else if (resinfo instanceof MultiResourceInfo) isRoot = ((MultiResourceInfo)resinfo).isRoot(); return getOptions(optionHolders, isRoot ? FILTER_PROJECT : FILTER_FILE); } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions() */ public Object[][] getOptions(IResourceConfiguration resConfig) { IHoldsOptions[] optionHolders = null; if (resConfig != null) { IHoldsOptions optionHolder = getOptionHolder(); if (optionHolder instanceof ITool) { optionHolders = resConfig.getTools(); } else if (optionHolder instanceof IToolChain) { // Resource configurations do not support categories that are children // of toolchains. The reason for this is that options in such categories // are intended to be global. Thus return nothing. // TODO: Remove this restriction in future? optionHolders = new IHoldsOptions[1]; optionHolders[0] = null; } // TODO: if further option holders were to be added in future, // this function needs to be extended } return getOptions(optionHolders, FILTER_FILE); } private IHoldsOptions getOptionHoldersSuperClass(IHoldsOptions optionHolder) { if (optionHolder instanceof ITool) return ((ITool)optionHolder).getSuperClass(); else if (optionHolder instanceof IToolChain) return ((IToolChain)optionHolder).getSuperClass(); return null; } private Object[][] getOptions(IHoldsOptions[] optionHolders, int filterValue) { IHoldsOptions catHolder = getOptionHolder(); IHoldsOptions optionHolder = null; if (optionHolders != null) { // Find the child of the configuration/resource configuration that represents the same tool. // It could the tool itself, or a "sub-class" of the tool. for (int i = 0; i < optionHolders.length; ++i) { IHoldsOptions current = optionHolders[i]; do { if (catHolder == current) { optionHolder = optionHolders[i]; break; } } while ((current = getOptionHoldersSuperClass(current)) != null); if (optionHolder != null) break; } } if (optionHolder == null) { optionHolder = catHolder; } // Get all of the tool's options and see which ones are part of // this category. IOption[] allOptions = optionHolder.getOptions(); Object[][] myOptions = new Object[allOptions.length][2]; int index = 0; for (int i = 0; i < allOptions.length; ++i) { IOption option = allOptions[i]; if (option.getCategory().equals(this)) { // Check whether this option can be displayed for a specific resource type. if( (option.getResourceFilter() == FILTER_ALL) || (option.getResourceFilter() == filterValue) ) { myOptions[index] = new Object[2]; myOptions[index][0] = optionHolder; myOptions[index][1] = option; index++; } } } return myOptions; } /* * M O D E L A T T R I B U T E A C C E S S O R S */ /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOwner() */ public IOptionCategory getOwner() { return owner; } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptionHolder() */ public IHoldsOptions getOptionHolder() { // This will stop at the parent's top category if (owner != null) return owner.getOptionHolder(); return holder; } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getTool() */ public ITool getTool() { // This will stop at the tool's top category IHoldsOptions parent = owner.getOptionHolder(); if (parent instanceof ITool) { return (ITool)parent; } return null; } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getIconPath() */ public URL getIconPath() { return iconPathURL; } /* * O B J E C T S T A T E M A I N T E N A N C E */ /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.core.IOptionCategory#isExtensionElement() */ public boolean isExtensionElement() { return isExtensionOptionCategory; } /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.core.IOptionCategory#isDirty() */ public boolean isDirty() { // This shouldn't be called for an extension OptionCategory if (isExtensionOptionCategory) return false; return isDirty; } /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.core.IIOptionCategory#setDirty(boolean) */ public void setDirty(boolean isDirty) { this.isDirty = isDirty; } public void resolveReferences() { boolean error = false; if (!resolved) { resolved = true; if (ownerId != null) { owner = holder.getOptionCategory(ownerId); if (owner == null) { if (holder instanceof IOptionCategory) { // Report error, only if the parent is a tool and thus also // an option category. ManagedBuildManager.outputResolveError( "owner", //$NON-NLS-1$ ownerId, "optionCategory", //$NON-NLS-1$ getId()); error = true; } else if ( false == holder.getId().equals(ownerId) ) { // Report error, if the holder ID does not match the owner's ID. ManagedBuildManager.outputResolveError( "owner", //$NON-NLS-1$ ownerId, "optionCategory", //$NON-NLS-1$ getId()); error = true; } } } if (owner == null) { owner = getNullOptionCategory(); } // Hook me in if (owner == null && error == false) ((HoldsOptions)holder).addChildCategory(this); else if (owner instanceof Tool) ((Tool)owner).addChildCategory(this); else ((OptionCategory)owner).addChildCategory(this); } } /** * @return Returns the managedBuildRevision. */ @Override public String getManagedBuildRevision() { if ( managedBuildRevision == null) { if ( getOptionHolder() != null) { return getOptionHolder().getManagedBuildRevision(); } } return managedBuildRevision; } /** * @return Returns the version. */ @Override public Version getVersion() { if ( version == null) { if ( getOptionHolder() != null) { return getOptionHolder().getVersion(); } } return version; } @Override public void setVersion(Version version) { // Do nothing } /** * Creates a name that uniquely identifies a category. The match name is * a concatenation of the tool and categories, e.g. Tool->Cat1->Cat2 * maps onto the string "Tool|Cat1|Cat2|" * * @param catOrTool category or tool for which to build the match name * @return match name */ static public String makeMatchName(IBuildObject catOrTool) { String catName = EMPTY_STRING; // Build the match name. do { catName = catOrTool.getName() + "|" + catName; //$NON-NLS-1$ if (catOrTool instanceof ITool) break; else if (catOrTool instanceof IOptionCategory) { catOrTool = ((IOptionCategory)catOrTool).getOwner(); } else break; } while (catOrTool != null); return catName; } /** * Finds an option category from an array of categories by comparing against * a match name. The match name is a concatenation of the tool and categories, * e.g. Tool->Cat1->Cat2 maps onto the string "Tool|Cat1|Cat2|" * * @param matchName an identifier to search * @param cats as returned by getChildCategories(), i.e. non-flattened * @return category or tool, if found and null otherwise */ static public Object findOptionCategoryByMatchName(String matchName, IOptionCategory[] cats) { Object primary = null; for (int j=0; j<cats.length; j++) { IBuildObject catOrTool = cats[j]; // Build the match name String catName = makeMatchName(catOrTool); // Check whether the name matches if (catName.equals(matchName)) { primary = cats[j]; break; } else if (matchName.startsWith(catName)) { // If there is a common root then check for any further children primary = findOptionCategoryByMatchName(matchName, cats[j].getChildCategories()); if (primary != null) break; } } return primary; } /* * (non-Javadoc) * * @see org.eclipse.cdt.managedbuilder.core.IOptionCategory#getApplicabilityCalculator() */ public IOptionCategoryApplicability getApplicabilityCalculator() { if (applicabilityCalculator == null) { if (applicabilityCalculatorElement != null) { try { if (applicabilityCalculatorElement.getAttribute(APPLICABILITY_CALCULATOR) != null) applicabilityCalculator = (IOptionCategoryApplicability) applicabilityCalculatorElement .createExecutableExtension(APPLICABILITY_CALCULATOR); } catch (CoreException e) { ManagedBuilderCorePlugin.log(e); } } } return applicabilityCalculator; } }