/*******************************************************************************
 *  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
 *     Baltasar Belyavsky (Texas Instruments) - [279633] Custom option command-generator support
 *     Miwako Tokugawa (Intel Corporation) - bug 222817 (OptionCategoryApplicability)
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.core;

import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.Vector;

import org.eclipse.cdt.build.internal.core.scannerconfig.CfgDiscoveredPathManager.PathInfoCache;
import org.eclipse.cdt.core.cdtvariables.CdtVariableException;
import org.eclipse.cdt.core.settings.model.ICStorageElement;
import org.eclipse.cdt.core.settings.model.extension.CLanguageData;
import org.eclipse.cdt.internal.core.SafeStringInterner;
import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyType;
import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue;
import org.eclipse.cdt.managedbuilder.core.BuildException;
import org.eclipse.cdt.managedbuilder.core.IAdditionalInput;
import org.eclipse.cdt.managedbuilder.core.IBuildObject;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IEnvVarBuildPath;
import org.eclipse.cdt.managedbuilder.core.IFileInfo;
import org.eclipse.cdt.managedbuilder.core.IFolderInfo;
import org.eclipse.cdt.managedbuilder.core.IHoldsOptions;
import org.eclipse.cdt.managedbuilder.core.IInputType;
import org.eclipse.cdt.managedbuilder.core.IManagedCommandLineGenerator;
import org.eclipse.cdt.managedbuilder.core.IManagedConfigElement;
import org.eclipse.cdt.managedbuilder.core.IManagedProject;
import org.eclipse.cdt.managedbuilder.core.IOption;
import org.eclipse.cdt.managedbuilder.core.IOptionApplicability;
import org.eclipse.cdt.managedbuilder.core.IOptionCategory;
import org.eclipse.cdt.managedbuilder.core.IOptionCategoryApplicability;
import org.eclipse.cdt.managedbuilder.core.IOptionCommandGenerator;
import org.eclipse.cdt.managedbuilder.core.IOptionPathConverter;
import org.eclipse.cdt.managedbuilder.core.IOutputType;
import org.eclipse.cdt.managedbuilder.core.IProjectType;
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.dataprovider.BuildEntryStorage;
import org.eclipse.cdt.managedbuilder.internal.dataprovider.BuildLanguageData;
import org.eclipse.cdt.managedbuilder.internal.enablement.OptionEnablementExpression;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildfileMacroSubstitutor;
import org.eclipse.cdt.managedbuilder.internal.macros.FileContextData;
import org.eclipse.cdt.managedbuilder.internal.macros.IMacroContextInfo;
import org.eclipse.cdt.managedbuilder.internal.macros.IMacroContextInfoProvider;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGenerator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGeneratorType;
import org.eclipse.cdt.utils.cdtvariables.CdtVariableResolver;
import org.eclipse.cdt.utils.cdtvariables.SupplierBasedCdtVariableSubstitutor;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeSettings;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.osgi.framework.Version;

/**
 * Represents a tool that can be invoked during a build.
 * Note that this class implements IOptionCategory to represent the top
 * category.
 */
public class Tool extends HoldsOptions implements ITool, IOptionCategory, IMatchKeyProvider<Tool>, IRealBuildObjectAssociation {

	public static final String DEFAULT_PATTERN = "${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}"; //$NON-NLS-1$
	public static final String DEFAULT_CBS_PATTERN = "${COMMAND}"; //$NON-NLS-1$

	//property name for holding the rebuild state
	private static final String REBUILD_STATE = "rebuildState";  //$NON-NLS-1$

	private static final String DEFAULT_SEPARATOR = ","; //$NON-NLS-1$
	//private static final IOptionCategory[] EMPTY_CATEGORIES = new IOptionCategory[0];
	//private static final IOption[] EMPTY_OPTIONS = new IOption[0];
	private static final String EMPTY_STRING = new String();
	private static final String EMPTY_QUOTED_STRING = "\"\""; //$NON-NLS-1$
	private static final String[] EMPTY_STRING_ARRAY = new String[0];
	private static final String DEFAULT_ANNOUNCEMENT_PREFIX = "Tool.default.announcement";	//$NON-NLS-1$
	private static final String WHITESPACE = " ";	//$NON-NLS-1$

	private static final boolean resolvedDefault = true;

	public static final String DEFAULT_TOOL_ID = "org.eclipse.cdt.build.core.default.tool"; //$NON-NLS-1$

	//  Superclass
	//  Note that superClass itself is defined in the base and that the methods
	//  getSuperClass() and setSuperClassInternal(), defined in Tool must be used to
	//  access it. This avoids widespread casts from IHoldsOptions to ITool.
	private String superClassId;
	//  Parent and children
	private IBuildObject parent;
	private Vector<InputType> inputTypeList;
	private Map<String, InputType> inputTypeMap;
	private Vector<OutputType> outputTypeList;
	private Map<String, OutputType> outputTypeMap;
	private List<IEnvVarBuildPath> envVarBuildPathList;
	//  Managed Build model attributes
	private String unusedChildren;
	private Boolean isAbstract;
	private String command;
	private List<String> inputExtensions;
	private List<String> interfaceExtensions;
	private Integer natureFilter;
	private String outputExtensions;
	private String outputFlag;
	private String outputPrefix;
	private String errorParserIds;
	private String commandLinePattern;
	private String versionsSupported;
	private String convertToId;
	private Boolean advancedInputCategory;
	private Boolean customBuildStep;
	private String announcement;
	private IConfigurationElement commandLineGeneratorElement = null;
	private IManagedCommandLineGenerator commandLineGenerator = null;
	private IConfigurationElement dependencyGeneratorElement = null;
	private IManagedDependencyGeneratorType dependencyGenerator = null;
	private URL iconPathURL;
	private IConfigurationElement pathconverterElement = null ;
	private IOptionPathConverter optionPathConverter = null ;
	private SupportedProperties supportedProperties;
	private Boolean supportsManagedBuild;
	private boolean isTest;
	//  Miscellaneous
	private boolean isExtensionTool = false;
	private boolean isDirty = false;
	private boolean resolved = resolvedDefault;
	private IConfigurationElement previousMbsVersionConversionElement = null;
	private IConfigurationElement currentMbsVersionConversionElement = null;
	private boolean rebuildState;
	private BooleanExpressionApplicabilityCalculator booleanExpressionCalculator;

	private HashMap<IInputType, CLanguageData> typeToDataMap = new HashMap<IInputType, CLanguageData>(2);
	private boolean fDataMapInited;
	private List<Tool> identicalList;
	private HashMap<String, PathInfoCache> discoveredInfoMap = new HashMap<String, PathInfoCache>(2);
	private String scannerConfigDiscoveryProfileId;

	/*
	 *  C O N S T R U C T O R S
	 */

	/**
	 * Constructor to create a tool based on an element from the plugin
	 * manifest.
	 *
	 * @param element The element containing the information about the tool.
	 * @param managedBuildRevision the fileVersion of Managed Build System
	 */
	public Tool(IManagedConfigElement element, String managedBuildRevision) {
		// setup for resolving
		super(false);
		resolved = false;

		isExtensionTool = true;

		// Set the managedBuildRevision
		setManagedBuildRevision(managedBuildRevision);

		loadFromManifest(element);

		// hook me up
		ManagedBuildManager.addExtensionTool(this);

		// set up the category map
		addOptionCategory(this);

		IManagedConfigElement enablements[] = element.getChildren(OptionEnablementExpression.NAME);
		if(enablements.length > 0)
			booleanExpressionCalculator = new BooleanExpressionApplicabilityCalculator(enablements);


		// Load children
		IManagedConfigElement[] toolElements = element.getChildren();
		for (int l = 0; l < toolElements.length; ++l) {
			IManagedConfigElement toolElement = toolElements[l];
			if (loadChild(toolElement)) {
				// do nothing
			} else if (toolElement.getName().equals(ITool.INPUT_TYPE)) {
				InputType inputType = new InputType(this, toolElement);
				addInputType(inputType);
			} else if (toolElement.getName().equals(ITool.OUTPUT_TYPE)) {
				OutputType outputType = new OutputType(this, toolElement);
				addOutputType(outputType);
			} else if (toolElement.getName().equals(IEnvVarBuildPath.BUILD_PATH_ELEMENT_NAME)){
				addEnvVarBuildPath(new EnvVarBuildPath(this,toolElement));
			} else if (toolElement.getName().equals(SupportedProperties.SUPPORTED_PROPERTIES)){
				loadProperties(toolElement);
			}
		}
	}

	/**
	 * Constructor to create a new tool for a tool-chain based on the information
	 * defined in the plugin.xml manifest.
	 *
	 * @param parent  The parent of this tool.  This can be a ToolChain or a
	 *                ResourceConfiguration.
	 * @param element The element containing the information about the tool.
	 * @param managedBuildRevision the fileVersion of Managed Build System
	 */
	public Tool(IBuildObject parent, IManagedConfigElement element, String managedBuildRevision) {
		this(element, managedBuildRevision);
		this.parent = parent;
	}

	/**
	 * This constructor is called to create a Tool whose attributes and children will be
	 * added by separate calls.
	 *
	 * @param parent - The parent of the tool, if any
	 * @param superClass - The superClass, if any
	 * @param Id - The id for the new tool
	 * @param name - The name for the new tool
	 * @param isExtensionElement - Indicates whether this is an extension element or a managed project element
	 */
	public Tool(ToolChain parent, ITool superClass, String Id, String name, boolean isExtensionElement) {
		super(resolvedDefault);
		this.parent = parent;
		setSuperClassInternal(superClass);
		setManagedBuildRevision(parent.getManagedBuildRevision());
		if (getSuperClass() != null) {
			superClassId = getSuperClass().getId();
		}

		setId(Id);
		setName(name);
		setVersion(getVersionFromId());

//		if(!superClass.isExtensionElement()){
//			((Tool)superClass).updateScannerInfoSettingsToInputTypes();
//		}

		isExtensionTool = isExtensionElement;
		if (isExtensionElement) {
			// Hook me up to the Managed Build Manager
			ManagedBuildManager.addExtensionTool(this);
		} else {
			setDirty(true);
			setRebuildState(true);
		}
	}

	/**
	 * This constructor is called to create a Tool whose attributes and children will be
	 * added by separate calls.
	 *
	 * @param parent - The parent of the tool, if any
	 * @param superClass - The superClass, if any
	 * @param Id - The id for the new tool
	 * @param name - The name for the new tool
	 * @param isExtensionElement - Indicates whether this is an extension element or a managed project element
	 */

	public Tool(ResourceConfiguration parent, ITool superClass, String Id, String name, boolean isExtensionElement) {
		super(resolvedDefault);
		this.parent = parent;
		setSuperClassInternal( superClass );
		setManagedBuildRevision(parent.getManagedBuildRevision());
		if (getSuperClass() != null) {
			superClassId = getSuperClass().getId();
		}
		setId(Id);
		setName(name);
		setVersion(getVersionFromId());

		isExtensionTool = isExtensionElement;
//		if(superClass != null && !superClass.isExtensionElement()){
//			((Tool)superClass).updateScannerInfoSettingsToInputTypes();
//		}
		if (isExtensionElement) {
			// Hook me up to the Managed Build Manager
			ManagedBuildManager.addExtensionTool(this);
		} else {
			setDirty(true);
			setRebuildState(true);
		}
	}

	/**
	 * Create a <code>Tool</code> based on the specification stored in the
	 * project file (.cdtbuild).
	 *
	 * @param parent The <code>IToolChain</code> or <code>IResourceConfiguration</code>
	 *               the tool will be added to.
	 * @param element The XML element that contains the tool settings.
	 * @param managedBuildRevision the fileVersion of Managed Build System
	 */
	public Tool(IBuildObject parent, ICStorageElement element, String managedBuildRevision) {
		super(resolvedDefault);
		this.parent = parent;
		isExtensionTool = false;

		// Set the managedBuildRevsion
		setManagedBuildRevision(managedBuildRevision);

		// Initialize from the XML attributes
		loadFromProject(element);

		// set up the category map
		addOptionCategory(this);

		// Load children
		ICStorageElement toolElements[] = element.getChildren();
		for (int i = 0; i < toolElements.length; ++i) {
			ICStorageElement toolElement = toolElements[i];
			if (loadChild(toolElement)) {
				// do nothing
			} else if (toolElement.getName().equals(ITool.INPUT_TYPE)) {
				InputType inputType = new InputType(this, toolElement);
				addInputType(inputType);
			} else if (toolElement.getName().equals(ITool.OUTPUT_TYPE)) {
				OutputType outputType = new OutputType(this, toolElement);
				addOutputType(outputType);
			}
		}

		String rebuild = PropertyManager.getInstance().getProperty(this, REBUILD_STATE);
		if(rebuild == null || Boolean.valueOf(rebuild).booleanValue())
			rebuildState = true;

	}

	/**
	 * Create a {@link Tool} based upon an existing tool.
	 *
	 * @param parent The {@link IToolChain} or {@link IResourceConfiguration}
	 *               the tool will be added to.
	 * @param toolSuperClass superclass of the tool
	 * @param Id The new Tool ID
	 * @param name The new Tool name
	 * @param tool The existing tool to clone.
	 */
	public Tool(IBuildObject parent, ITool toolSuperClass, String Id, String name, Tool tool){
		this(parent, toolSuperClass.getId(), Id, name, tool);

		setSuperClassInternal(toolSuperClass);
	}

	public Tool(IBuildObject parent, String toolSuperClassId, String Id, String name, Tool tool){
		super(resolvedDefault);
		this.parent = parent;
//		if (toolSuperClass != null) {
//			setSuperClassInternal( toolSuperClass );
//		} else {
//			setSuperClassInternal( tool.getSuperClass() );
//		}
//		if (getSuperClass() != null) {
			superClassId = toolSuperClassId;//getSuperClass().getId();
//		}
		setId(Id);
		setName(name);

		// Set the managedBuildRevision & the version
		setManagedBuildRevision(tool.getManagedBuildRevision());
		setVersion(getVersionFromId());

		isExtensionTool = false;
		boolean copyIds = Id.equals(tool.id);

		//  Copy the remaining attributes
		if(tool.versionsSupported != null) {
			versionsSupported = new String(tool.versionsSupported);
		}
		if(tool.convertToId != null) {
			convertToId = new String(tool.convertToId);
		}
		if (tool.unusedChildren != null) {
			unusedChildren = new String(tool.unusedChildren);
		}
		if (tool.errorParserIds != null) {
			errorParserIds = new String(tool.errorParserIds);
		}
		if (tool.isAbstract != null) {
			isAbstract = new Boolean(tool.isAbstract.booleanValue());
		}
		if (tool.command != null) {
			command = new String(tool.command);
		}
		if (tool.commandLinePattern != null) {
			commandLinePattern = new String(tool.commandLinePattern);
		}
		if (tool.inputExtensions != null) {
			inputExtensions = new ArrayList<String>(tool.inputExtensions);
		}
		if (tool.interfaceExtensions != null) {
			interfaceExtensions = new ArrayList<String>(tool.interfaceExtensions);
		}
		if (tool.natureFilter != null) {
			natureFilter = new Integer(tool.natureFilter.intValue());
		}
		if (tool.outputExtensions != null) {
			outputExtensions = new String(tool.outputExtensions);
		}
		if (tool.outputFlag != null) {
			outputFlag = new String(tool.outputFlag);
		}
		if (tool.outputPrefix != null) {
			outputPrefix = new String(tool.outputPrefix);
		}
		if (tool.advancedInputCategory != null) {
			advancedInputCategory = new Boolean(tool.advancedInputCategory.booleanValue());
		}
		if (tool.customBuildStep != null) {
			customBuildStep = new Boolean(tool.customBuildStep.booleanValue());
		}
		if (tool.announcement != null) {
			announcement = new String(tool.announcement);
		}
       	supportsManagedBuild = tool.supportsManagedBuild;


		commandLineGenerator = tool.commandLineGenerator;
		if(commandLineGenerator == null) {
			// only need XML if the generator hasn't been created yet
			commandLineGeneratorElement = tool.commandLineGeneratorElement;
		}

		dependencyGenerator = tool.dependencyGenerator;
		if(dependencyGenerator == null) {
			// only need XML if the generator hasn't been created yet
			dependencyGeneratorElement = tool.dependencyGeneratorElement;
		}

		pathconverterElement = tool.pathconverterElement ;
		optionPathConverter = tool.optionPathConverter ;

		if(tool.envVarBuildPathList != null)
			envVarBuildPathList = new ArrayList<IEnvVarBuildPath>(tool.envVarBuildPathList);

//		tool.updateScannerInfoSettingsToInputTypes();

		//  Clone the children in superclass
		super.copyChildren(tool);
		//  Clone the children
		if (tool.inputTypeList != null) {
			@SuppressWarnings("unchecked")
			HashMap<String, PathInfoCache> clone = (HashMap<String, PathInfoCache>)tool.discoveredInfoMap.clone();
			discoveredInfoMap = clone;
			for (InputType inputType : tool.getInputTypeList()) {
				PathInfoCache cache = discoveredInfoMap.remove(getTypeKey(inputType));
				int nnn = ManagedBuildManager.getRandomNumber();
				String subId;
				String subName;
				if (inputType.getSuperClass() != null) {
					subId = copyIds ? inputType.id : inputType.getSuperClass().getId() + "." + nnn;		//$NON-NLS-1$
					subName = inputType.getSuperClass().getName();
				} else {
					subId = copyIds ? inputType.id : inputType.getId() + "." + nnn;		//$NON-NLS-1$
					subName = inputType.getName();
				}
				InputType newInputType = new InputType(this, subId, subName, inputType);
				addInputType(newInputType);
				if(cache != null){
					discoveredInfoMap.put(getTypeKey(newInputType), cache);
				}
			}
		}
		if (tool.outputTypeList != null) {
			for (OutputType outputType : tool.getOutputTypeList()) {
				int nnn = ManagedBuildManager.getRandomNumber();
				String subId;
				String subName;
				if (outputType.getSuperClass() != null) {
					subId = copyIds ? outputType.id : outputType.getSuperClass().getId() + "." + nnn;		//$NON-NLS-1$
					subName = outputType.getSuperClass().getName();
				} else {
					subId = copyIds ? outputType.id : outputType.getId() + "." + nnn;		//$NON-NLS-1$
					subName = outputType.getName();
				}
				OutputType newOutputType = new OutputType(this, subId, subName, outputType);
				addOutputType(newOutputType);
			}
		}

		// icon
		if ( tool.iconPathURL != null ) {
			iconPathURL = tool.iconPathURL;
		}

		if(copyIds){
			isDirty = tool.isDirty;
			rebuildState = tool.rebuildState;
		} else{
	        setDirty(true);
			setRebuildState(true);
		}
	}

	void copyNonoverriddenSettings(Tool tool){
		if(name == null)
			setName(tool.name);

		// Set the managedBuildRevision & the version
//		setManagedBuildRevision(tool.getManagedBuildRevision());
//		setVersion(getVersionFromId());

		//  Copy the remaining attributes
		if(versionsSupported == null) {
			versionsSupported = tool.versionsSupported;
		}
		if(convertToId == null) {
			convertToId = tool.convertToId;
		}
		if (unusedChildren == null) {
			unusedChildren = tool.unusedChildren;
		}
		if (errorParserIds == null) {
			errorParserIds = tool.errorParserIds;
		}
		if (isAbstract == null) {
			isAbstract = tool.isAbstract;
		}
		if (command == null) {
			command = tool.command;
		}
		if (commandLinePattern == null) {
			commandLinePattern = tool.commandLinePattern;
		}
		if (inputExtensions == null && tool.inputExtensions != null) {
			inputExtensions = new ArrayList<String>(tool.inputExtensions);
		}
		if (interfaceExtensions == null && tool.interfaceExtensions != null) {
			interfaceExtensions = new ArrayList<String>(tool.interfaceExtensions);
		}
		if (natureFilter == null) {
			natureFilter = tool.natureFilter;
		}
		if (outputExtensions == null) {
			outputExtensions = tool.outputExtensions;
		}
		if (outputFlag == null) {
			outputFlag = tool.outputFlag;
		}
		if (outputPrefix == null) {
			outputPrefix = tool.outputPrefix;
		}
		if (advancedInputCategory == null) {
			advancedInputCategory = tool.advancedInputCategory;
		}
		if (customBuildStep == null) {
			customBuildStep = tool.customBuildStep;
		}
		if (announcement == null) {
			announcement = tool.announcement;
		}

		if(supportsManagedBuild == null)
			supportsManagedBuild = tool.supportsManagedBuild;

		if(commandLineGenerator == null) {
			commandLineGenerator = tool.commandLineGenerator;
			// only copy the generator element if we don't already have a generator
			if(commandLineGenerator == null && commandLineGeneratorElement == null)
				commandLineGeneratorElement = tool.commandLineGeneratorElement;
		}

		if(dependencyGenerator == null) {
			dependencyGenerator = tool.dependencyGenerator;

			// only copy the generator element if we don't already have a generator
			if(dependencyGenerator == null) {
				if(dependencyGeneratorElement == null)
					dependencyGeneratorElement = tool.dependencyGeneratorElement;
			}
		}

		if(optionPathConverter == null) {
			optionPathConverter = tool.optionPathConverter ;

			if(optionPathConverter == null) {
				if(pathconverterElement == null)
					pathconverterElement = tool.pathconverterElement ;
			}
		}

		if(envVarBuildPathList == null && tool.envVarBuildPathList != null)
			envVarBuildPathList = new ArrayList<IEnvVarBuildPath>(tool.envVarBuildPathList);

		//  Clone the children in superclass
		super.copyNonoverriddenSettings(tool);
		//  Clone the children
		if (inputTypeList == null && tool.inputTypeList != null) {
			for (InputType inputType : tool.getInputTypeList()) {
				int nnn = ManagedBuildManager.getRandomNumber();
				String subId;
				String subName;
				if (inputType.getSuperClass() != null) {
					subId = inputType.getSuperClass().getId() + "." + nnn;		//$NON-NLS-1$
					subName = inputType.getSuperClass().getName();
				} else {
					subId = inputType.getId() + "." + nnn;		//$NON-NLS-1$
					subName = inputType.getName();
				}
				InputType newInputType = new InputType(this, subId, subName, inputType);
				addInputType(newInputType);
			}
		}
		if (outputTypeList == null && tool.outputTypeList != null) {
			for (OutputType outputType : tool.getOutputTypeList()) {
				int nnn = ManagedBuildManager.getRandomNumber();
				String subId;
				String subName;
				if (outputType.getSuperClass() != null) {
					subId = outputType.getSuperClass().getId() + "." + nnn;		//$NON-NLS-1$
					subName = outputType.getSuperClass().getName();
				} else {
					subId = outputType.getId() + "." + nnn;		//$NON-NLS-1$
					subName = outputType.getName();
				}
				OutputType newOutputType = new OutputType(this, subId, subName, outputType);
				addOutputType(newOutputType);
			}
		}

		// icon
		if ( iconPathURL == null ) {
			iconPathURL = tool.iconPathURL;
		}

        setDirty(true);
		setRebuildState(true);
	}

	/*
	 *  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
	 */

	/* (non-Javadoc)
	 * Load the tool information from the XML element specified in the
	 * argument
	 * @param element An XML element containing the tool information
	 */
	protected void loadFromManifest(IManagedConfigElement element) {
		// setup for resolving
		ManagedBuildManager.putConfigElement(this, element);

		// id
		setId(SafeStringInterner.safeIntern(element.getAttribute(IBuildObject.ID)));

		// name
		setName(SafeStringInterner.safeIntern(element.getAttribute(IBuildObject.NAME)));

		// version
		setVersion(getVersionFromId());

		// superClass
		superClassId = SafeStringInterner.safeIntern(element.getAttribute(IProjectType.SUPERCLASS));

		// Get the unused children, if any
		unusedChildren = SafeStringInterner.safeIntern(element.getAttribute(IProjectType.UNUSED_CHILDREN));

		// Get the 'versionsSupported' attribute
		versionsSupported = SafeStringInterner.safeIntern(element.getAttribute(VERSIONS_SUPPORTED));

		// Get the 'convertToId' attribute
		convertToId = SafeStringInterner.safeIntern(element.getAttribute(CONVERT_TO_ID));

		// isAbstract
        String isAbs = element.getAttribute(IProjectType.IS_ABSTRACT);
        if (isAbs != null){
    		isAbstract = new Boolean("true".equals(isAbs)); //$NON-NLS-1$
        }

		// Get the semicolon separated list of IDs of the error parsers
		errorParserIds = SafeStringInterner.safeIntern(SafeStringInterner.safeIntern(element.getAttribute(IToolChain.ERROR_PARSERS)));

		// Get the nature filter
		String nature = element.getAttribute(NATURE);
		if (nature != null) {
			if ("both".equals(nature)) {	//$NON-NLS-1$
				natureFilter = new Integer(FILTER_BOTH);
			} else if ("cnature".equals(nature)) {	//$NON-NLS-1$
				natureFilter = new Integer(FILTER_C);
			} else if ("ccnature".equals(nature)) {	//$NON-NLS-1$
				natureFilter = new Integer(FILTER_CC);
			} else {
				natureFilter = new Integer(FILTER_BOTH);
			}
		}

		// Get the supported input file extensions
		String inputs = element.getAttribute(ITool.SOURCES);
		if (inputs != null) {
			StringTokenizer tokenizer = new StringTokenizer(inputs, DEFAULT_SEPARATOR);
			while (tokenizer.hasMoreElements()) {
				getInputExtensionsList().add(SafeStringInterner.safeIntern(tokenizer.nextToken()));
			}
		}

		// Get the interface (header file) extensions
		String headers = element.getAttribute(INTERFACE_EXTS);
		if (headers != null) {
			StringTokenizer tokenizer = new StringTokenizer(headers, DEFAULT_SEPARATOR);
			while (tokenizer.hasMoreElements()) {
				getInterfaceExtensionsList().add(SafeStringInterner.safeIntern(tokenizer.nextToken()));
			}
		}

		// Get the output extension
		outputExtensions = SafeStringInterner.safeIntern(element.getAttribute(ITool.OUTPUTS));

		// Get the tool invocation command
		command = SafeStringInterner.safeIntern(element.getAttribute(ITool.COMMAND));

		// Get the flag to control output
		outputFlag = SafeStringInterner.safeIntern(element.getAttribute(ITool.OUTPUT_FLAG));

		// Get the output prefix
		outputPrefix = SafeStringInterner.safeIntern(element.getAttribute(ITool.OUTPUT_PREFIX));

		// Get command line pattern
		commandLinePattern = SafeStringInterner.safeIntern(element.getAttribute( ITool.COMMAND_LINE_PATTERN ));

		// Get advancedInputCategory
        String advInput = element.getAttribute(ITool.ADVANCED_INPUT_CATEGORY);
        if (advInput != null){
			advancedInputCategory = new Boolean("true".equals(advInput)); //$NON-NLS-1$
        }

		// Get customBuildStep
        String cbs = element.getAttribute(ITool.CUSTOM_BUILD_STEP);
        if (cbs != null){
			customBuildStep = new Boolean("true".equals(cbs)); //$NON-NLS-1$
        }

		// Get the announcement text
		announcement = SafeStringInterner.safeIntern(element.getAttribute(ITool.ANNOUNCEMENT));

		// Store the configuration element IFF there is a command line generator defined
		String commandLineGenerator = element.getAttribute(COMMAND_LINE_GENERATOR);
		if (commandLineGenerator != null && element instanceof DefaultManagedConfigElement) {
			commandLineGeneratorElement = ((DefaultManagedConfigElement)element).getConfigurationElement();
		}

		// Store the configuration element IFF there is a dependency generator defined
		String depGenerator = element.getAttribute(DEP_CALC_ID);
		if (depGenerator != null && element instanceof DefaultManagedConfigElement) {
			dependencyGeneratorElement = ((DefaultManagedConfigElement)element).getConfigurationElement();
		}

		// icon
		if ( element.getAttribute(IOptionCategory.ICON) != null && element instanceof DefaultManagedConfigElement)
		{
		    String icon = element.getAttribute(IOptionCategory.ICON);
			iconPathURL = ManagedBuildManager.getURLInBuildDefinitions( (DefaultManagedConfigElement)element, new Path(icon) );
		}

		// optionPathConverter
		String pathconverterTypeName = element.getAttribute(ITool.OPTIONPATHCONVERTER);
		if (pathconverterTypeName != null && element instanceof DefaultManagedConfigElement) {
			pathconverterElement = ((DefaultManagedConfigElement)element).getConfigurationElement();
		}

		String tmp = element.getAttribute(SUPPORTS_MANAGED_BUILD);
		if(tmp != null)
			supportsManagedBuild = Boolean.valueOf(tmp);

		scannerConfigDiscoveryProfileId = SafeStringInterner.safeIntern(element.getAttribute(IToolChain.SCANNER_CONFIG_PROFILE_ID));

        tmp = element.getAttribute(IS_SYSTEM);
        if(tmp != null)
        	isTest = Boolean.valueOf(tmp).booleanValue();
	}

	/* (non-Javadoc)
	 * Initialize the tool information from the XML element
	 * specified in the argument
	 *
	 * @param element An XML element containing the tool 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)));
		}

		// version
		setVersion(getVersionFromId());

		// superClass
		superClassId = SafeStringInterner.safeIntern(element.getAttribute(IProjectType.SUPERCLASS));

		// Get the unused children, if any
		if (element.getAttribute(IProjectType.UNUSED_CHILDREN) != null) {
			unusedChildren = SafeStringInterner.safeIntern(element.getAttribute(IProjectType.UNUSED_CHILDREN));
		}

		// isAbstract
		if (element.getAttribute(IProjectType.IS_ABSTRACT) != null) {
			String isAbs = element.getAttribute(IProjectType.IS_ABSTRACT);
			if (isAbs != null){
				isAbstract = new Boolean("true".equals(isAbs)); //$NON-NLS-1$
			}
		}

		// Get the 'versionSupported' attribute
		if (element.getAttribute(VERSIONS_SUPPORTED) != null) {
			versionsSupported = SafeStringInterner.safeIntern(element.getAttribute(VERSIONS_SUPPORTED));
		}

		// Get the 'convertToId' id
		if (element.getAttribute(CONVERT_TO_ID) != null) {
			convertToId = SafeStringInterner.safeIntern(element.getAttribute(CONVERT_TO_ID));
		}

		// Get the semicolon separated list of IDs of the error parsers
		if (element.getAttribute(IToolChain.ERROR_PARSERS) != null) {
			errorParserIds = SafeStringInterner.safeIntern(element.getAttribute(IToolChain.ERROR_PARSERS));
		}

		// Get the nature filter
		if (element.getAttribute(NATURE) != null) {
			String nature = element.getAttribute(NATURE);
			if (nature != null) {
				if ("both".equals(nature)) {	//$NON-NLS-1$
					natureFilter = new Integer(FILTER_BOTH);
				} else if ("cnature".equals(nature)) {	//$NON-NLS-1$
					natureFilter = new Integer(FILTER_C);
				} else if ("ccnature".equals(nature)) {	//$NON-NLS-1$
					natureFilter = new Integer(FILTER_CC);
				} else {
					natureFilter = new Integer(FILTER_BOTH);
				}
			}
		}

		// Get the supported input file extension
		if (element.getAttribute(ITool.SOURCES) != null) {
			String inputs = element.getAttribute(ITool.SOURCES);
			if (inputs != null) {
				StringTokenizer tokenizer = new StringTokenizer(inputs, DEFAULT_SEPARATOR);
				while (tokenizer.hasMoreElements()) {
					getInputExtensionsList().add(SafeStringInterner.safeIntern(tokenizer.nextToken()));
				}
			}
		}

		// Get the interface (header file) extensions
		if (element.getAttribute(INTERFACE_EXTS) != null) {
			String headers = element.getAttribute(INTERFACE_EXTS);
			if (headers != null) {
				StringTokenizer tokenizer = new StringTokenizer(headers, DEFAULT_SEPARATOR);
				while (tokenizer.hasMoreElements()) {
					getInterfaceExtensionsList().add(SafeStringInterner.safeIntern(tokenizer.nextToken()));
				}
			}
		}

		// Get the output extension
		if (element.getAttribute(ITool.OUTPUTS) != null) {
			outputExtensions = SafeStringInterner.safeIntern(element.getAttribute(ITool.OUTPUTS));
		}

		// Get the tool invocation command
		if (element.getAttribute(ITool.COMMAND) != null) {
			command = SafeStringInterner.safeIntern(element.getAttribute(ITool.COMMAND));
		}

		// Get the flag to control output
		if (element.getAttribute(ITool.OUTPUT_FLAG) != null) {
			outputFlag = SafeStringInterner.safeIntern(element.getAttribute(ITool.OUTPUT_FLAG));
		}

		// Get the output prefix
		if (element.getAttribute(ITool.OUTPUT_PREFIX) != null) {
			outputPrefix = SafeStringInterner.safeIntern(element.getAttribute(ITool.OUTPUT_PREFIX));
		}

		// Get command line pattern
		if( element.getAttribute( ITool.COMMAND_LINE_PATTERN ) != null) {
			commandLinePattern = SafeStringInterner.safeIntern(element.getAttribute( ITool.COMMAND_LINE_PATTERN ));
		}

		// advancedInputCategory
		if (element.getAttribute(ITool.ADVANCED_INPUT_CATEGORY) != null) {
			String advInput = element.getAttribute(ITool.ADVANCED_INPUT_CATEGORY);
			if (advInput != null){
				advancedInputCategory = new Boolean("true".equals(advInput)); //$NON-NLS-1$
			}
		}

		// customBuildStep
		if (element.getAttribute(ITool.CUSTOM_BUILD_STEP) != null) {
			String cbs = element.getAttribute(ITool.CUSTOM_BUILD_STEP);
			if (cbs != null){
				customBuildStep = new Boolean("true".equals(cbs)); //$NON-NLS-1$
			}
		}

		// Get the announcement text
		if (element.getAttribute(ITool.ANNOUNCEMENT) != null) {
			announcement = SafeStringInterner.safeIntern(element.getAttribute(ITool.ANNOUNCEMENT));
		}

		// 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;
			}
		}

		scannerConfigDiscoveryProfileId = SafeStringInterner.safeIntern(element.getAttribute(IToolChain.SCANNER_CONFIG_PROFILE_ID));
	}

	void resolveProjectReferences(boolean onLoad){
		if (superClassId != null && superClassId.length() > 0) {
			ITool tool = ManagedBuildManager.getExtensionTool(superClassId);
			if(tool == null){
				Configuration cfg = (Configuration)getParentResourceInfo().getParent();
				tool = cfg.findToolById(superClassId);
			}
			if(tool != null)
				superClass = tool;

			if(onLoad){
				// Check for migration support
				checkForMigrationSupport();
			}
		}
	}

	private void loadProperties(IManagedConfigElement el){
		supportedProperties = new SupportedProperties(el);
	}

	/**
	 * Persist the tool to the project file.
	 */
	@Override
	public void serialize(ICStorageElement element) {
		try {
			if (getSuperClass() != null)
				element.setAttribute(IProjectType.SUPERCLASS, getSuperClass().getId());

			// id
			element.setAttribute(IBuildObject.ID, id);

			// name
			if (name != null) {
				element.setAttribute(IBuildObject.NAME, name);
			}

			// unused children
			if (unusedChildren != null) {
				element.setAttribute(IProjectType.UNUSED_CHILDREN, unusedChildren);
			}

			// isAbstract
			if (isAbstract != null) {
				element.setAttribute(IProjectType.IS_ABSTRACT, isAbstract.toString());
			}

			// versionsSupported
			if (versionsSupported != null) {
				element.setAttribute(VERSIONS_SUPPORTED, versionsSupported);
			}

			// convertToId
			if (convertToId != null) {
				element.setAttribute(CONVERT_TO_ID, convertToId);
			}

			// error parsers
			if (errorParserIds != null) {
				element.setAttribute(IToolChain.ERROR_PARSERS, errorParserIds);
			}

			// nature filter
			if (natureFilter != null) {
				String nature;
				if (natureFilter.intValue() == FILTER_C) {
					nature = "cnature";	//$NON-NLS-1$
				} else if (natureFilter.intValue() == FILTER_CC) {
					nature = "ccnature";	//$NON-NLS-1$
				} else {
					nature = "both";	//$NON-NLS-1$
				}
				element.setAttribute(NATURE, nature);
			}

			// input file extensions
			if (getInputExtensionsList().size() > 0) {
				Iterator<String> iter = getInputExtensionsList().listIterator();
				String inputs = iter.next();
				while (iter.hasNext()) {
					inputs += DEFAULT_SEPARATOR;
					inputs += iter.next();
				}
				element.setAttribute(ITool.SOURCES, inputs);
			}

			// interface (header file) extensions
			if (getInterfaceExtensionsList().size() > 0) {
				Iterator<String> iter = getInterfaceExtensionsList().listIterator();
				String headers = iter.next();
				while (iter.hasNext()) {
					headers += DEFAULT_SEPARATOR;
					headers += iter.next();
				}
				element.setAttribute(INTERFACE_EXTS, headers);
			}

			// output extension
			if (outputExtensions != null) {
				element.setAttribute(ITool.OUTPUTS, outputExtensions);
			}

			// command
			if (command != null) {
				element.setAttribute(ITool.COMMAND, command);
			}

			// flag to control output
			if (outputFlag != null) {
				element.setAttribute(ITool.OUTPUT_FLAG, outputFlag);
			}

			// output prefix
			if (outputPrefix != null) {
				element.setAttribute(ITool.OUTPUT_PREFIX, outputPrefix);
			}

			// command line pattern
			if (commandLinePattern != null) {
				element.setAttribute(ITool.COMMAND_LINE_PATTERN, commandLinePattern);
			}

			// advancedInputCategory
			if (advancedInputCategory != null) {
				element.setAttribute(ITool.ADVANCED_INPUT_CATEGORY, advancedInputCategory.toString());
			}

			// customBuildStep
			if (customBuildStep != null) {
				element.setAttribute(ITool.CUSTOM_BUILD_STEP, customBuildStep.toString());
			}

			// announcement text
			if (announcement != null) {
				element.setAttribute(ITool.ANNOUNCEMENT, announcement);
			}

			// Serialize elements from my super class
			super.serialize(element);

			// Serialize my children
			for (InputType type : getInputTypeList())
				type.serialize(element.createChild(INPUT_TYPE));

			for (OutputType type : getOutputTypeList())
				type.serialize(element.createChild(OUTPUT_TYPE));

			// Note: command line generator cannot be specified in a project file because
			//       an IConfigurationElement is needed to load it!
			if (commandLineGeneratorElement != null) {
				//  TODO:  issue warning?
			}

			// Note: dependency generator cannot be specified in a project file because
			//       an IConfigurationElement is needed to load it!
			if (dependencyGeneratorElement != null) {
				//  TODO:  issue warning?
			}

			if (iconPathURL != null) {
				// Save as URL in string form
				element.setAttribute(IOptionCategory.ICON, iconPathURL.toString());
			}

			// Note: optionPathConverter cannot be specified in a project file because
			//       an IConfigurationElement is needed to load it!
			if (pathconverterElement != null) {
				//  TODO:  issue warning?
			}

			if(scannerConfigDiscoveryProfileId != null)
				element.setAttribute(IToolChain.SCANNER_CONFIG_PROFILE_ID, scannerConfigDiscoveryProfileId);

			saveRebuildState();

			// I am clean now
			isDirty = false;
		} catch (Exception e) {
			// TODO: issue an error message
		}
	}

	/*
	 *  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.ITool#getParent()
	 */
	@Override
	public IBuildObject getParent() {
		return parent;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setParent(IBuildObject)
	 */
	public void setToolParent(IBuildObject newParent) {
		this.parent = newParent;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getTopOptionCategory()
	 */
	@Override
	public IOptionCategory getTopOptionCategory() {
		return this;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#createInputType(IInputType, String, String, boolean)
	 */
	@Override
	public IInputType createInputType(IInputType superClass, String Id, String name, boolean isExtensionElement) {
		InputType type = superClass == null || superClass.isExtensionElement() ?
				new InputType(this, superClass, Id, name, isExtensionElement)
				: new InputType(this, Id, name, (InputType)superClass);

		if(superClass != null){
			BuildLanguageData data = (BuildLanguageData)typeToDataMap.remove(superClass);
			if(data != null){
				data.updateInputType(type);
				typeToDataMap.put(type, data);
			}
		}
		addInputType(type);
		setDirty(true);

		return type;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#removeInputType(IInputType)
	 */
	@Override
	public void removeInputType(IInputType type) {
		getInputTypeList().remove(type);
		getInputTypeMap().remove(type.getId());
		setDirty(true);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getInputTypes()
	 */
	@Override
	public IInputType[] getInputTypes() {
		IInputType[] types = getAllInputTypes();
		return filterInputTypes(types);
	}

	public IInputType[] getAllInputTypes() {
		IInputType[] types = null;
		// Merge our input types with our superclass' input types.
		if (getSuperClass() != null) {
			types = ((Tool)getSuperClass()).getAllInputTypes();
		}
		// Our options take precedence.
		Vector<InputType> ourTypes = getInputTypeList();
		if (types != null) {
			// Avoid replacing a replacement. See bug 303735
			boolean[] typesWasReplaced = new boolean[types.length];

			for (int i = 0; i < ourTypes.size(); i++) {
				IInputType ourType = ourTypes.get(i);
				int j;
				for (j = 0; j < types.length; j++) {
					IInputType otherTypeToCheck = ManagedBuildManager.getExtensionInputType(types[j]);
					if(otherTypeToCheck == null)
						otherTypeToCheck = types[j];

					if (ourType.getSuperClass() != null &&
					    ourType.getSuperClass().getId().equals(otherTypeToCheck.getId()) &&
					    !typesWasReplaced[j]) {
						types[j] = ourType;
						typesWasReplaced[j] = true;
						break;
					}
				}
				//  No Match?  Add it.
				if (j == types.length) {
					IInputType[] newTypes = new IInputType[types.length + 1];
					boolean[] newTypesWasReplaced = new boolean[types.length + 1];
					for (int k = 0; k < types.length; k++) {
						newTypes[k] = types[k];
						newTypesWasReplaced[k] = typesWasReplaced[k];
					}
					newTypes[j] = ourType;
					types = newTypes;
					typesWasReplaced = newTypesWasReplaced;
				}
			}
		} else {
			types = ourTypes.toArray(new IInputType[ourTypes.size()]);
		}
		return types;
	}


	private boolean hasInputTypes() {
		if (getInputTypeList().size() > 0)
			return true;
		return false;
	}

	@Override
	public IInputType getInputTypeById(String id) {
		InputType type = (InputType)getAllInputTypeById(id);

		if(isExtensionTool || type == null || type.isEnabled(this))
			return type;
		return null;
	}

	public IInputType getAllInputTypeById(String id) {
		IInputType type = getInputTypeMap().get(id);
		if (type == null) {
			if (getSuperClass() != null) {
				return ((Tool)getSuperClass()).getAllInputTypeById(id);
			}
		}

		return type;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#createOutputType(IOutputType, String, String, boolean)
	 */
	@Override
	public IOutputType createOutputType(IOutputType superClass, String Id, String name, boolean isExtensionElement) {
		OutputType type = new OutputType(this, superClass, Id, name, isExtensionElement);
		addOutputType(type);
		setDirty(true);
		return type;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#removeOutputType(IOutputType)
	 */
	@Override
	public void removeOutputType(IOutputType type) {
		getOutputTypeList().remove(type);
		getOutputTypeMap().remove(type.getId());
		setDirty(true);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getOutputTypes()
	 */
	@Override
	public IOutputType[] getOutputTypes() {
		IOutputType[] types = getAllOutputTypes();

		return filterOutputTypes(types);
	}

	public IOutputType[] getAllOutputTypes() {
		IOutputType[] types = null;
		// Merge our output types with our superclass' output types.
		if (getSuperClass() != null) {
			types = ((Tool)getSuperClass()).getAllOutputTypes();
		}
		// Our options take precedence.
		Vector<OutputType> ourTypes = getOutputTypeList();
		if (types != null) {
			for (int i = 0; i < ourTypes.size(); i++) {
				IOutputType ourType = ourTypes.get(i);
				int j;
				for (j = 0; j < types.length; j++) {
					if (ourType.getSuperClass() != null &&
					    ourType.getSuperClass().getId().equals(types[j].getId())) {
						types[j] = ourType;
						break;
					}
				}
				//  No Match?  Add it.
				if (j == types.length) {
					IOutputType[] newTypes = new IOutputType[types.length + 1];
					for (int k = 0; k < types.length; k++) {
						newTypes[k] = types[k];
					}
					newTypes[j] = ourType;
					types = newTypes;
				}
			}
		} else {
			types = ourTypes.toArray(new IOutputType[ourTypes.size()]);
		}
		return types;
	}

	private IOutputType[] filterOutputTypes(IOutputType types[]){
		if(isExtensionTool || types.length == 0)
			return types;

		List<OutputType> list = new ArrayList<OutputType>(types.length);
		for (IOutputType itype : types) {
			OutputType type = (OutputType)itype;
			if(type.isEnabled(this))
				list.add(type);
		}

		return list.toArray(new OutputType[list.size()]);
	}

	private IInputType[] filterInputTypes(IInputType types[]){
		if(isExtensionTool || types.length == 0)
			return types;

		List<InputType> list = new ArrayList<InputType>(types.length);
		for (IInputType itype : types) {
			InputType type = (InputType)itype;
			if(type.isEnabled(this))
				list.add(type);
		}

		return list.toArray(new InputType[list.size()]);
	}

	private boolean hasOutputTypes() {
		Vector<OutputType> ourTypes = getOutputTypeList();
		if (ourTypes.size() > 0) return true;
		return false;
	}

	@Override
	public IOutputType getPrimaryOutputType() {
		IOutputType type = null;
		IOutputType[] types = getOutputTypes();
		if (types != null && types.length > 0) {
			for (int i=0; i<types.length; i++) {
				if (i == 0) type = types[0];
				if (types[i].getPrimaryOutput() == true) {
					type = types[i];
					break;
				}
			}
		}
		return type;
	}

	@Override
	public IOutputType getOutputTypeById(String id) {
		OutputType type = (OutputType)getAllOutputTypeById(id);

		if(isExtensionTool || type == null || type.isEnabled(this))
			return type;
		return null;
	}

	public IOutputType getAllOutputTypeById(String id) {
		IOutputType type = getOutputTypeMap().get(id);
		if (type == null) {
			if (getSuperClass() != null) {
				return ((Tool)getSuperClass()).getAllOutputTypeById(id);
			}
		}
		return type;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOwner()
	 */
	@Override
	public IOptionCategory getOwner() {
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getIconPath()
	 */
	@Override
	public URL getIconPath() {
		if (iconPathURL == null  &&  getSuperClass() != null) {
			return getSuperClass().getTopOptionCategory().getIconPath();
		}
		return iconPathURL;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions()
	 */
	@Override
	public Object[][] getOptions(IConfiguration configuration, IHoldsOptions optionHolder) {
		if (optionHolder != this) return null;
		return getOptions(configuration);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions()
	 */
	@Override
	public Object[][] getOptions(IConfiguration configuration) {
		// Find the child of the configuration that represents the same tool.
		// It could be the tool itself, or a "sub-class" of the tool.
		if (configuration != null) {
			ITool[] tools = configuration.getTools();
			return getOptions(tools);
		} else {
			return getAllOptions(this);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions()
	 */
	@Override
	public Object[][] getOptions(IResourceInfo resInfo, IHoldsOptions optionHolder) {
		if (optionHolder != this) return null;
		return getOptions(resInfo);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptions()
	 */
	@Override
	public Object[][] getOptions(IResourceConfiguration resConfig) {
		ITool[] tools = resConfig.getTools();
		return getOptions(tools);
	}

	public Object[][] getOptions(IResourceInfo resConfig) {
		ITool[] tools = resConfig.getTools();
		return getOptions(tools);
	}

	private Object[][] getOptions(ITool[] tools) {
		ITool catTool = this;
		ITool tool = null;
		for (ITool curTool : tools) {
			ITool superTool = curTool;
			do {
				if (catTool == superTool) {
					tool = curTool;
					break;
				}
			} while ((superTool = superTool.getSuperClass()) != null);
			if (tool != null) break;
		}
		// Get all of the tool's options and see which ones are part of
		// this category.
		if( tool == null)
			return null;

		return getAllOptions(tool);
	}

	private Object[][] getAllOptions(ITool tool) {
		IOption[] allOptions = tool.getOptions();
		Object[][] myOptions = new Object[allOptions.length][2];
		int index = 0;
		for (IOption option : allOptions) {
			IOptionCategory optCat = option.getCategory();
			if (optCat instanceof ITool) {
				//  Determine if the category is this tool or a superclass
				ITool current = this;
				boolean match = false;
				do {
					if (optCat == current) {
						match = true;
						break;
					}
				} while ((current = current.getSuperClass()) != null);
				if (match) {
					myOptions[index] = new Object[2];
					myOptions[index][0] = tool;
					myOptions[index][1] = option;
					index++;
				}
			}
		}

		return myOptions;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getTool()
	 */
	@Override
	public ITool getTool() {
		return this;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.IOptionCategory#getOptionHolder()
	 */
	@Override
	public IHoldsOptions getOptionHolder() {
		return this;
	}

	/* (non-Javadoc)
	 * Memory-safe way to access the list of input types
	 */
	private Vector<InputType> getInputTypeList() {
		if (inputTypeList == null) {
			inputTypeList = new Vector<InputType>();
		}
		return inputTypeList;
	}

	/* (non-Javadoc)
	 * Memory-safe way to access the list of IDs to input types
	 */
	private Map<String, InputType> getInputTypeMap() {
		if (inputTypeMap == null) {
			inputTypeMap = new HashMap<String, InputType>();
		}
		return inputTypeMap;
	}

	public void addInputType(InputType type) {
		getInputTypeList().add(type);
		getInputTypeMap().put(type.getId(), type);
	}

	/* (non-Javadoc)
	 * Memory-safe way to access the list of output types
	 */
	private Vector<OutputType> getOutputTypeList() {
		if (outputTypeList == null) {
			outputTypeList = new Vector<OutputType>();
		}
		return outputTypeList;
	}

	/* (non-Javadoc)
	 * Memory-safe way to access the list of IDs to output types
	 */
	private Map<String, OutputType> getOutputTypeMap() {
		if (outputTypeMap == null) {
			outputTypeMap = new HashMap<String, OutputType>();
		}
		return outputTypeMap;
	}

	public void addOutputType(OutputType type) {
		getOutputTypeList().add(type);
		getOutputTypeMap().put(type.getId(), type);
	}

	/*
	 *  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.managedbuilder.core.ITool#getSuperClass()
	 */
	@Override
	public ITool getSuperClass() {
		return (ITool)superClass;
	}

	/* (non-Javadoc)
	 * Access function to set the superclass element that is defined in
	 * the base class.
	 */
	private void setSuperClassInternal(ITool superClass) {
		this.superClass = superClass;
	}

	public void setSuperClass(ITool superClass) {
		if ( this.superClass != superClass ) {
			this.superClass = superClass;
			if ( this.superClass == null) {
				superClassId = null;
			} else {
				superClassId = this.superClass.getId();
			}

			if(!isExtensionElement())
				setDirty(true);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getName()
	 */
	@Override
	public String getName() {
		return (name == null && getSuperClass() != null) ? getSuperClass().getName() : name;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#isAbstract()
	 */
	@Override
	public boolean isAbstract() {
		if (isAbstract != null) {
			return isAbstract.booleanValue();
		} else {
			return false;	// Note: no inheritance from superClass
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setIsAbstract(boolean)
	 */
	@Override
	public void setIsAbstract(boolean b) {
		isAbstract = new Boolean(b);
		setDirty(true);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getUnusedChildren()
	 */
	@Override
	public String getUnusedChildren() {
		if (unusedChildren != null) {
			return unusedChildren;
		} else
			return EMPTY_STRING;	// Note: no inheritance from superClass
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getErrorParserIds()
	 */
	@Override
	public String getErrorParserIds() {
		String ids = errorParserIds;
		if (ids == null) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				ids = getSuperClass().getErrorParserIds();
			}
		}
		return ids;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getErrorParserList()
	 */
	@Override
	public String[] getErrorParserList() {
		String parserIDs = getErrorParserIds();
		String[] errorParsers;
		if (parserIDs != null) {
			// Check for an empty string
			if (parserIDs.length() == 0) {
				errorParsers = new String[0];
			} else {
				StringTokenizer tok = new StringTokenizer(parserIDs, ";"); //$NON-NLS-1$
				List<String> list = new ArrayList<String>(tok.countTokens());
				while (tok.hasMoreElements()) {
					list.add(tok.nextToken());
				}
				String[] strArr = {""};	//$NON-NLS-1$
				errorParsers = list.toArray(strArr);
			}
		} else {
			errorParsers = new String[0];
		}
		return errorParsers;
	}

	public Set<String> contributeErrorParsers(Set<String> set){
		if(getErrorParserIds() != null){
			if(set == null)
				set = new HashSet<String>();
			String ids[] = getErrorParserList();
			if(ids.length != 0)
				set.addAll(Arrays.asList(ids));
		}
		return set;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getInputExtensions()
	 * @deprecated
	 */
	@Override
	public List<String> getInputExtensions() {
		String[] exts = getPrimaryInputExtensions();
		List<String> extList = new ArrayList<String>();
		for (String ext : exts) {
			extList.add(ext);
		}
		return extList;
	}

	private List<String> getInputExtensionsAttribute() {
		if( (inputExtensions == null) || ( inputExtensions.size() == 0) ) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return ((Tool)getSuperClass()).getInputExtensionsAttribute();
			} else {
				inputExtensions = new ArrayList<String>();
			}
		}
		return inputExtensions;
	}

	private List<String> getInputExtensionsList() {
		if (inputExtensions == null) {
				inputExtensions = new ArrayList<String>();
		}
		return inputExtensions;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getDefaultInputExtension()
	 */
	@Override
	public String getDefaultInputExtension() {
		// Find the primary input type
		IInputType type = getPrimaryInputType();
		if (type != null) {
			String[] exts = type.getSourceExtensions(this);
			// Use the first entry in the list
			if (exts.length > 0) return exts[0];
		}
		// If none, use the input extensions specified for the Tool (backwards compatibility)
		List<String> extsList = getInputExtensionsAttribute();
		// Use the first entry in the list
		if (extsList != null && extsList.size() > 0) return extsList.get(0);
		return EMPTY_STRING;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getPrimaryInputExtensions()
	 */
	@Override
	public String[] getPrimaryInputExtensions() {
		IInputType type = getPrimaryInputType();
		if (type != null) {
			String[] exts = type.getSourceExtensions(this);
			// Use the first entry in the list
			if (exts.length > 0) return exts;
		}
		// If none, use the input extensions specified for the Tool (backwards compatibility)
		List<String> extsList = getInputExtensionsAttribute();
		// Use the first entry in the list
		if (extsList != null && extsList.size() > 0) {
			return extsList.toArray(new String[extsList.size()]);
		}
		return EMPTY_STRING_ARRAY;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getAllInputExtensions()
	 */
	@Override
	public String[] getAllInputExtensions() {
		return getAllInputExtensions(getProject());
	}

	public String[] getAllInputExtensions(IProject project) {
		IInputType[] types = getInputTypes();
		if (types != null && types.length > 0) {
			List<String> allExts = new ArrayList<String>();
			for (IInputType type : types) {
				String[] exts = ((InputType)type).getSourceExtensions(this, project);
				for (String ext : exts) {
					allExts.add(ext);
				}
			}
			if (allExts.size() > 0) {
				return allExts.toArray(new String[allExts.size()]);
			}
		}
		// If none, use the input extensions specified for the Tool (backwards compatibility)
		List<String> extsList = getInputExtensionsAttribute();
		if (extsList != null && extsList.size() > 0) {
			return extsList.toArray(new String[extsList.size()]);
		}
		return EMPTY_STRING_ARRAY;
	}

	@Override
	public IInputType getPrimaryInputType() {
		IInputType type = null;
		IInputType[] types = getInputTypes();
		if (types != null && types.length > 0) {
			for (int i=0; i<types.length; i++) {
				if (i == 0) type = types[0];
				if (types[i].getPrimaryInput() == true) {
					type = types[i];
					break;
				}
			}
		}
		return type;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getInputInputType()
	 */
	@Override
	public IInputType getInputType(String inputExtension) {
		return getInputType(inputExtension, getProject());
	}

	public IInputType getInputType(String inputExtension, IProject project) {
		IInputType type = null;
		IInputType[] types = getInputTypes();
		if (types != null && types.length > 0) {
			for (IInputType t : types) {
				if (((InputType)t).isSourceExtension(this, inputExtension, project)) {
					type = t;
					break;
				}
			}
		}
		return type;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getAdditionalDependencies()
	 */
	@Override
	public IPath[] getAdditionalDependencies() {
		List<IPath> allDeps = new ArrayList<IPath>();
		IInputType[] types = getInputTypes();
		for (IInputType type : types) {
			//  Additional dependencies come from 2 places.
			//  1.  From AdditionalInput childen
			for (IPath p : type.getAdditionalDependencies())
				allDeps.add(p);

			//  2.  From InputTypes that other than the primary input type
			if (type != getPrimaryInputType()) {
				if (type.getOptionId() != null) {
					IOption option = getOptionBySuperClassId(type.getOptionId());
					if (option != null) {
						try {
							List<IPath> inputs = new ArrayList<IPath>();
							int optType = option.getValueType();
							if (optType == IOption.STRING) {
								inputs.add(Path.fromOSString(option.getStringValue()));
							} else if (
									optType == IOption.STRING_LIST ||
									optType == IOption.LIBRARIES ||
									optType == IOption.OBJECTS ||
									optType == IOption.INCLUDE_FILES ||
									optType == IOption.LIBRARY_PATHS ||
									optType == IOption.LIBRARY_FILES ||
									optType == IOption.MACRO_FILES
									) {
								@SuppressWarnings("unchecked")
								List<String> inputNames = (List<String>)option.getValue();
								filterValues(optType, inputNames);
								for (String s : inputNames)
									inputs.add(Path.fromOSString(s));
							}
							allDeps.addAll(inputs);
						} catch( BuildException ex ) {
						}
					}
				} else if (type.getBuildVariable() != null && type.getBuildVariable().length() > 0) {
					allDeps.add(Path.fromOSString("$(" + type.getBuildVariable() + ")"));   //$NON-NLS-1$ //$NON-NLS-2$
				}
			}
		}
		return allDeps.toArray(new IPath[allDeps.size()]);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getAdditionalResources()
	 */
	@Override
	public IPath[] getAdditionalResources() {
		List<IPath> allRes = new ArrayList<IPath>();
		for (IInputType type : getInputTypes()) {
			//  Additional resources come from 2 places.
			//  1.  From AdditionalInput childen
			for (IPath r : type.getAdditionalResources())
				allRes.add(r);

			//  2.  From InputTypes that other than the primary input type
			if (type != getPrimaryInputType()) {
				String var = type.getBuildVariable();
				if (var != null && var.length() > 0) {
					allRes.add(Path.fromOSString("$(" + type.getBuildVariable() + ")"));   //$NON-NLS-1$ //$NON-NLS-2$
				}
			}
		}
		return allRes.toArray(new IPath[allRes.size()]);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getAllDependencyExtensions()
	 */
	@Override
	public String[] getAllDependencyExtensions() {
		IInputType[] types = getInputTypes();
		if (types != null && types.length > 0) {
			List<String> allExts = new ArrayList<String>();
			for (IInputType t : types)
				for (String s : t.getDependencyExtensions(this))
					allExts.add(s);

			if (allExts.size() > 0)
				return allExts.toArray(new String[allExts.size()]);
		}
		// If none, use the header extensions specified for the Tool (backwards compatibility)
		List<String> extsList = getHeaderExtensionsAttribute();
		if (extsList != null && extsList.size() > 0) {
			return extsList.toArray(new String[extsList.size()]);
		}
		return EMPTY_STRING_ARRAY;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getInterfaceExtension()
	 * @deprecated
	 */
	@Override
	public List<String> getInterfaceExtensions() {
		return getHeaderExtensionsAttribute();
	}

	private List<String> getHeaderExtensionsAttribute() {
		if (interfaceExtensions == null || interfaceExtensions.size() == 0) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return ((Tool)getSuperClass()).getHeaderExtensionsAttribute();
			} else {
			    if (interfaceExtensions == null) {
			        interfaceExtensions = new ArrayList<String>();
			    }
			}
		}
		return interfaceExtensions;
	}

	private List<String> getInterfaceExtensionsList() {
		if (interfaceExtensions == null) {
			interfaceExtensions = new ArrayList<String>();
		}
		return interfaceExtensions;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getOutputFlag()
	 */
	@Override
	public String getOutputFlag() {
		if (outputFlag == null) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return getSuperClass().getOutputFlag();
			} else {
				return EMPTY_STRING;
			}
		}
		return outputFlag;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getOutputPrefix()
	 */
	@Override
	public String getOutputPrefix() {
		// Get the outputPrefix from an OutputType, if any.
		IOutputType type = null;
		IOutputType[] types = getOutputTypes();
		if (types != null && types.length > 0) {
			for (int i=0; i<types.length; i++) {
				if (i == 0) type = types[0];
				if (types[i].getPrimaryOutput() == true) {
					type = types[i];
					break;
				}
			}
		}
		if (type != null) {
			return type.getOutputPrefix();
		}

		// If there are no OutputTypes, use the deprecated Tool attribute
		if (outputPrefix == null) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return getSuperClass().getOutputPrefix();
			} else {
				return EMPTY_STRING;
			}
		}
		return outputPrefix;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getToolCommand()
	 */
	@Override
	public String getToolCommand() {
		if (command == null) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return getSuperClass().getToolCommand();
			} else {
				return EMPTY_STRING;
			}
		}
		return command;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getCommandLinePattern()
	 */
	@Override
	public String getCommandLinePattern() {
		if (commandLinePattern == null) {
			if (getSuperClass() != null) {
				return getSuperClass().getCommandLinePattern();
			} else {
				if (getCustomBuildStep()) {
					return new String(DEFAULT_CBS_PATTERN);  // Default pattern
				} else {
					return new String(DEFAULT_PATTERN);  // Default pattern
				}
			}
		}
		return commandLinePattern;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getAdvancedInputCategory()
	 */
	@Override
	public boolean getAdvancedInputCategory() {
		if (advancedInputCategory == null) {
			if (getSuperClass() != null) {
				return getSuperClass().getAdvancedInputCategory();
			} else {
				return false;	// default is false
			}
		}
		return advancedInputCategory.booleanValue();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getCustomBuildStep()
	 */
	@Override
	public boolean getCustomBuildStep() {
		if (customBuildStep == null) {
			if (getSuperClass() != null) {
				return getSuperClass().getCustomBuildStep();
			} else {
				return false;	// default is false
			}
		}
		return customBuildStep.booleanValue();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getAnnouncement()
	 */
	@Override
	public String getAnnouncement() {
		String an = getAnnouncementAttribute();
		if(an == null){
			an = ManagedMakeMessages.getResourceString(DEFAULT_ANNOUNCEMENT_PREFIX) +
						WHITESPACE + getName();  // + "(" + getId() + ")";
		}
		return an;
	}

	public String getAnnouncementAttribute() {
		if (announcement == null) {
			if (getSuperClass() != null) {
				return ((Tool)getSuperClass()).getAnnouncementAttribute();
			}
			return null;
		}
		return announcement;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getCommandLineGeneratorElement()
	 */
	public IConfigurationElement getCommandLineGeneratorElement() {
		if (commandLineGeneratorElement == null) {
			if (getSuperClass() != null) {
				return ((Tool)getSuperClass()).getCommandLineGeneratorElement();
			}
		}
		return commandLineGeneratorElement;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setCommandLineGeneratorElement(String)
	 */
	public void setCommandLineGeneratorElement(IConfigurationElement element) {
		commandLineGeneratorElement = element;
		setDirty(true);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getCommandLineGenerator()
	 */
	@Override
	public IManagedCommandLineGenerator getCommandLineGenerator() {
		if (commandLineGenerator != null) {
			return commandLineGenerator;
		}
		IConfigurationElement element = getCommandLineGeneratorElement();
		if (element != null) {
			try {
				if (element.getAttribute(COMMAND_LINE_GENERATOR) != null) {
					commandLineGenerator = (IManagedCommandLineGenerator) element.createExecutableExtension(COMMAND_LINE_GENERATOR);
					commandLineGeneratorElement = null; // no longer needed now that we've created one
					return commandLineGenerator;
				}
			} catch (CoreException e) {}
		}
		return ManagedCommandLineGenerator.getCommandLineGenerator();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getDependencyGeneratorElement()
	 * @deprecated
	 */
	public IConfigurationElement getDependencyGeneratorElement() {
		//  First try the primary InputType
		IInputType type = getPrimaryInputType();
		if (type != null) {
			IConfigurationElement primary = ((InputType)type).getDependencyGeneratorElement();
			if (primary != null) return primary;
		}

		//  If not found, use the deprecated attribute
		return getToolDependencyGeneratorElement();
	}


	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getDependencyGeneratorElementForExtension()
	 */
	public IConfigurationElement getDependencyGeneratorElementForExtension(String sourceExt) {
		IInputType[] types = getInputTypes();
		if (types != null) {
			for (IInputType type : types) {
				if (type.isSourceExtension(this, sourceExt)) {
					return ((InputType)type).getDependencyGeneratorElement();
				}
			}
		}

		//  If not found, use the deprecated attribute
		return getToolDependencyGeneratorElement();
	}

	private IConfigurationElement getToolDependencyGeneratorElement() {
		if (dependencyGeneratorElement == null) {
			if (getSuperClass() != null) {
				return ((Tool)getSuperClass()).getToolDependencyGeneratorElement();
			}
		}
		return dependencyGeneratorElement;
	}

//	/* (non-Javadoc)
//	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setDependencyGeneratorElement(String)
//	 * @deprecated
//	 */
//	private void setDependencyGeneratorElement(IConfigurationElement element) {
//		dependencyGeneratorElement = element;
//		setDirty(true);
//	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getDependencyGenerator()
	 * @deprecated
	 */
	@Override
	public IManagedDependencyGenerator getDependencyGenerator() {
		if (dependencyGenerator != null) {
			if (dependencyGenerator instanceof IManagedDependencyGenerator)
				return (IManagedDependencyGenerator)dependencyGenerator;
			else
				return null;
		}
		IConfigurationElement element = getDependencyGeneratorElement();
		if (element != null) {
			try {
				if (element.getAttribute(DEP_CALC_ID) != null) {
					dependencyGenerator = (IManagedDependencyGeneratorType) element.createExecutableExtension(DEP_CALC_ID);
					if (dependencyGenerator != null) {
						if (dependencyGenerator instanceof IManagedDependencyGenerator) {
							dependencyGeneratorElement = null; // no longer needed now that we've created one
							return (IManagedDependencyGenerator)dependencyGenerator;
						}
						else
							return null;
					}
				}
			} catch (CoreException e) {}
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getDependencyGeneratorForExtension()
	 */
	@Override
	public IManagedDependencyGeneratorType getDependencyGeneratorForExtension(String sourceExt) {
		if (dependencyGenerator != null) {
			return dependencyGenerator;
		}
		IConfigurationElement element = getDependencyGeneratorElementForExtension(sourceExt);
		if (element != null) {
			try {
				if (element.getAttribute(DEP_CALC_ID) != null) {
					dependencyGenerator = (IManagedDependencyGeneratorType) element.createExecutableExtension(DEP_CALC_ID);
					return dependencyGenerator;
				}
			} catch (CoreException e) {}
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getNatureFilter()
	 */
	@Override
	public int getNatureFilter() {
		if (natureFilter == null) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return getSuperClass().getNatureFilter();
			} else {
				return FILTER_BOTH;
			}
		}
		return natureFilter.intValue();
	}

	public String[] getAllOutputExtensions(IProject project) {
		IOutputType[] types = getOutputTypes();
		if (types != null && types.length > 0) {
			List<String> allExts = new ArrayList<String>();
			for (IOutputType t : types) {
				String[] exts = ((OutputType)t).getOutputExtensions(this, project);
				if (exts != null)
					for (String s : exts)
						allExts.add(s);
			}
			if (allExts.size() > 0)
				return allExts.toArray(new String[allExts.size()]);
		}
		// If none, use the outputs specified for the Tool (backwards compatibility)
		String[] extsList = getOutputsAttribute();
		if (extsList != null && extsList.length > 0)
			return extsList;
		else
			return EMPTY_STRING_ARRAY;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getAllOutputExtensions()
	 */
	@Override
	public String[] getAllOutputExtensions() {
		return getAllOutputExtensions(getProject());
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getOutputExtensions()
	 * @deprecated
	 */
	@Override
	public String[] getOutputExtensions() {
		return getOutputsAttribute();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getOutputsAttribute()
	 */
	@Override
	public String[] getOutputsAttribute() {
		// TODO:  Why is this treated differently than inputExtensions?
		if (outputExtensions == null) {
			if (getSuperClass() != null) {
				return getSuperClass().getOutputsAttribute();
			} else {
				return null;
			}
		}
		return outputExtensions.split(DEFAULT_SEPARATOR);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getOutputExtension(java.lang.String)
	 */
	@Override
	public String getOutputExtension(String inputExtension) {
		// Search thru the output-types to find one that has a primary input type with this extension
		IOutputType[] types = getOutputTypes();
		int i;
		if (types != null) {
			for (i=0; i<types.length; i++) {
				IInputType inputType = types[i].getPrimaryInputType();
				if (inputType != null && inputType.isSourceExtension(this, inputExtension)) {
					String[] exts = types[i].getOutputExtensions(this);
					if (exts != null && exts.length > 0) {
						return exts[0];
					}
				}
			}
			// Does any input type produce this extension?
			if (getInputType(inputExtension) != null) {
				//  Return the first extension of the primary output type
				IOutputType outType = getPrimaryOutputType();
				String[] exts = outType.getOutputExtensions(this);
				if (exts != null && exts.length > 0) {
					return exts[0];
				}
			}
		}
		// If no OutputTypes specified, examine the list of input extensions
		String[] inputExts = getAllInputExtensions();
		for (i=0; i<inputExts.length; i++) {
			if (inputExts[i].equals(inputExtension)) {
				String[] exts = getOutputsAttribute();
				if (exts != null) {
					if (i < exts.length) {
						return exts[i];
					} else {
						return exts[exts.length - 1];
					}
				}
			}
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getOutputType(java.lang.String)
	 */
	@Override
	public IOutputType getOutputType(String outputExtension) {
		IOutputType type = null;
		IOutputType[] types = getOutputTypes();
		if (types != null && types.length > 0) {
			for (IOutputType t : types) {
				if (t.isOutputExtension(this, outputExtension)) {
					type = t;
					break;
				}
			}
		}
		return type;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setErrorParserIds()
	 */
	@Override
	public void setErrorParserIds(String ids) {
		String currentIds = getErrorParserIds();
		if (ids == null && currentIds == null) return;
		if (currentIds == null || ids == null || !(currentIds.equals(ids))) {
			errorParserIds = ids;
			isDirty = true;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setToolCommand(java.lang.String)
	 */
	@Override
	public boolean setToolCommand(String cmd) {
		if (cmd == null && command == null) return false;
		if (cmd == null || command == null || !cmd.equals(command)) {
			command = cmd;
			isDirty = true;
			setRebuildState(true);
			return true;
		} else {
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setCommandLinePattern()
	 */
	@Override
	public void setCommandLinePattern(String pattern) {
		if (pattern == null && commandLinePattern == null) return;
		if (pattern == null || commandLinePattern == null || !pattern.equals(commandLinePattern)) {
			commandLinePattern = pattern;
			setRebuildState(true);
			isDirty = true;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setOutputFlag(java.lang.String)
	 */
	@Override
	public void setOutputFlag(String flag) {
		if (flag == null && outputFlag == null) return;
		if (outputFlag == null || flag == null || !(flag.equals(outputFlag))) {
			outputFlag = flag;
			setRebuildState(true);
			isDirty = true;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setOutputPrefix(java.lang.String)
	 */
	@Override
	public void setOutputPrefix(String prefix) {
		if (prefix == null && outputPrefix == null) return;
		if (outputPrefix == null || prefix == null || !(prefix.equals(outputPrefix))) {
			outputPrefix = prefix;
			setRebuildState(true);
			isDirty = true;
		}
	}

	@Override
	public void setOutputPrefixForPrimaryOutput(String prefix) {
		if(prefix != null && prefix.equals(getOutputPrefix()))
			return;

		IOutputType type = getPrimaryOutputType();
		if(type == null)
			setOutputPrefix(prefix);
		else {
			setOutputPrefixForType(type, prefix);
		}
	}

	private void setOutputPrefixForType(IOutputType type, String prefix){
		if(prefix == null){
			if(type.getParent() != this)
				return;
		}
		type = getEditableOutputType(type);
		type.setOutputPrefix(prefix);
		setRebuildState(true);
		isDirty = true;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setOutputsAttribute(java.lang.String)
	 */
	@Override
	public void setOutputsAttribute(String ext) {
		if (ext == null && outputExtensions == null) return;
		if (outputExtensions == null || ext == null || !(ext.equals(outputExtensions))) {
			outputExtensions = ext;
			isDirty = true;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setAdvancedInputCategory(boolean)
	 */
	@Override
	public void setAdvancedInputCategory(boolean b) {
		if (advancedInputCategory == null || !(b == advancedInputCategory.booleanValue())) {
			advancedInputCategory = new Boolean(b);
			setDirty(true);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setCustomBuildStep(boolean)
	 */
	@Override
	public void setCustomBuildStep(boolean b) {
		if (customBuildStep == null || !(b == customBuildStep.booleanValue())) {
			customBuildStep = new Boolean(b);
			setDirty(true);
		}
	}

	@Override
	public void setAnnouncement(String newText) {
		if (newText == null && announcement == null) return;
		if (announcement == null || newText == null || !(newText.equals(announcement))) {
			announcement = newText;
			setDirty(true);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getCommandFlags()
	 */
	@Override
	public String[] getCommandFlags() throws BuildException {
		return getToolCommandFlags(null,null);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#getToolFlags()
	 */
	@Override
	public String getToolFlags() throws BuildException {
		return getToolCommandFlagsString(null,null);
	}

	/**
	 * This method used internally by the Tool to obtain the command flags with the build macros resolved,
	 * but could be also used by other MBS components to adjust the tool flags resolution
	 * behavior by passing the method some custom macro substitutor
	 *
	 * @return the command flags with the build macros resolved
	 */
	public String[] getToolCommandFlags(IPath inputFileLocation, IPath outputFileLocation,
				SupplierBasedCdtVariableSubstitutor macroSubstitutor,
				IMacroContextInfoProvider provider) {
		IOption[] opts = getOptions();
		ArrayList<String> flags = new ArrayList<String>();
		StringBuilder sb = new StringBuilder();
		for (IOption option : opts) {
			if (option == null)
				continue;
			sb.setLength( 0 );

			// check to see if the option has an applicability calculator
			IOptionApplicability applicabilityCalculator = option.getApplicabilityCalculator();
			IOptionCategory cat = option.getCategory();
			IOptionCategoryApplicability catApplicabilityCalculator = cat.getApplicabilityCalculator();

			IBuildObject config = null;
			IBuildObject parent = getParent();
			if ( parent instanceof IResourceConfiguration ) {
				config = parent;
			} else if ( parent instanceof IToolChain ){
				config = ((IToolChain)parent).getParent();
			}

			if ((catApplicabilityCalculator==null || catApplicabilityCalculator.isOptionCategoryVisible(config, this, cat))
			 && (applicabilityCalculator == null || applicabilityCalculator.isOptionUsedInCommandLine(config, this, option))) {

				// update option in case when its value changed.
				// This code is added to fix bug #219684 and
				// avoid using "getOptionToSet()"
				if (applicabilityCalculator != null &&
					!(applicabilityCalculator instanceof BooleanExpressionApplicabilityCalculator)) {
					if (option.getSuperClass() != null)
						option = getOptionBySuperClassId(option.getSuperClass().getId());
					else
						option = getOptionById(option.getId());
				}

				try{
				boolean generateDefaultCommand = true;
				IOptionCommandGenerator commandGenerator = option.getCommandGenerator();
				if(commandGenerator != null) {
					IMacroContextInfo info = provider.getMacroContextInfo(BuildMacroProvider.CONTEXT_FILE, new FileContextData(inputFileLocation, outputFileLocation, option, this));
					if(info != null) {
						macroSubstitutor.setMacroContextInfo(info);
						String command = commandGenerator.generateCommand(option, macroSubstitutor);
						if(command != null) {
							sb.append(command);
							generateDefaultCommand = false;
						}
					}
				}
				if(generateDefaultCommand) {
				switch (option.getValueType()) {
				case IOption.BOOLEAN :
					String boolCmd;
					if (option.getBooleanValue()) {
						boolCmd = option.getCommand();
					} else {
						// Note: getCommandFalse is new with CDT 2.0
						boolCmd = option.getCommandFalse();
					}
					if (boolCmd != null && boolCmd.length() > 0) {
						sb.append(boolCmd);
					}
					break;

				case IOption.ENUMERATED :
					String enumVal = option.getEnumCommand(option.getSelectedEnum());
					if (enumVal.length() > 0) {
						sb.append(enumVal);
					}
					break;

				case IOption.TREE :
					String treeVal = option.getCommand(option.getStringValue());
					if (treeVal.length() > 0) {
						sb.append(treeVal);
					}
					break;

				case IOption.STRING :{
						String strCmd = option.getCommand();
						String val = option.getStringValue();
						IMacroContextInfo info = provider.getMacroContextInfo(IBuildMacroProvider.CONTEXT_FILE, new FileContextData(inputFileLocation, outputFileLocation, option, this));
						if(info != null){
							macroSubstitutor.setMacroContextInfo(info);
							if (val.length() > 0
								&& (val = CdtVariableResolver.resolveToString(val, macroSubstitutor)).length() > 0) {
								sb.append( evaluateCommand( strCmd, val ) );
							}
						}
					}
					break;

				case IOption.STRING_LIST :
				case IOption.INCLUDE_FILES :
				case IOption.INCLUDE_PATH :
				case IOption.LIBRARY_PATHS :
				case IOption.LIBRARY_FILES :
				case IOption.MACRO_FILES :
				case IOption.UNDEF_INCLUDE_FILES :
				case IOption.UNDEF_INCLUDE_PATH :
				case IOption.UNDEF_LIBRARY_PATHS :
				case IOption.UNDEF_LIBRARY_FILES :
				case IOption.UNDEF_MACRO_FILES :				{
						String listCmd = option.getCommand();
						IMacroContextInfo info = provider.getMacroContextInfo(IBuildMacroProvider.CONTEXT_FILE,
								new FileContextData(inputFileLocation, outputFileLocation, option, this));
						if(info != null){
							macroSubstitutor.setMacroContextInfo(info);
							String[] list = CdtVariableResolver.resolveStringListValues(option.getBasicStringListValue(), macroSubstitutor, true);
							if(list != null){
								for (String temp : list) {
									if(temp.length() > 0 && !temp.equals(EMPTY_QUOTED_STRING))
										sb.append( evaluateCommand( listCmd, temp ) + WHITE_SPACE );
								}
							}
						}
					}
					break;

				case IOption.PREPROCESSOR_SYMBOLS :
				case IOption.UNDEF_PREPROCESSOR_SYMBOLS :{
					String defCmd = option.getCommand();
					IMacroContextInfo info = provider.getMacroContextInfo(IBuildMacroProvider.CONTEXT_FILE,
							new FileContextData(inputFileLocation, outputFileLocation, option, this));
					if(info != null){
						macroSubstitutor.setMacroContextInfo(info);
						String[] symbols = CdtVariableResolver.resolveStringListValues(option.getBasicStringListValue(), macroSubstitutor, true);
						if(symbols != null){
							for (String temp : symbols) {
								if(temp.length() > 0)
									sb.append( evaluateCommand( defCmd, temp ) + WHITE_SPACE);
							}
						}
					}
				}
					break;

				default :
					break;
				}
				}

				if (sb.toString().trim().length() > 0)
					flags.add(sb.toString().trim());

				} catch (BuildException e) {
					// Bug 315187 one broken option shouldn't cascade to all other options breaking the build...
					Status s = new Status(IStatus.ERROR, ManagedBuilderCorePlugin.getUniqueIdentifier(), MessageFormat.format(ManagedMakeMessages.getString("Tool_Problem_Discovering_Args_For_Option"), option, //$NON-NLS-1$
							option.getId()), e);
					ManagedBuilderCorePlugin.log(new CoreException(s));
				} catch (CdtVariableException e) {
					Status s = new Status(IStatus.ERROR, ManagedBuilderCorePlugin.getUniqueIdentifier(), MessageFormat.format(ManagedMakeMessages.getString("Tool_Problem_Discovering_Args_For_Option"), option, //$NON-NLS-1$
							option.getId()), e);
					ManagedBuilderCorePlugin.log(new CoreException(s));
				}
			}
		}
		String[] f = new String[ flags.size() ];
		return flags.toArray( f );
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getToolCommandFlags(org.eclipse.core.runtime.IPath, org.eclipse.core.runtime.IPath)
	 */
	@Override
	public String[] getToolCommandFlags(IPath inputFileLocation, IPath outputFileLocation) throws BuildException{
		SupplierBasedCdtVariableSubstitutor macroSubstitutor = new BuildfileMacroSubstitutor(null,EMPTY_STRING,WHITE_SPACE);
		return getToolCommandFlags(inputFileLocation, outputFileLocation, macroSubstitutor, BuildMacroProvider.getDefault() );
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getToolCommandFlagsString(org.eclipse.core.runtime.IPath, org.eclipse.core.runtime.IPath)
	 */
	@Override
	public String getToolCommandFlagsString(IPath inputFileLocation, IPath outputFileLocation) throws BuildException{
		// Get all of the optionList
		StringBuilder buf = new StringBuilder();
		String[] flags = getToolCommandFlags(inputFileLocation,outputFileLocation);
		for (String flag : flags) {
			if( flag != null ) {
				buf.append( flag + WHITE_SPACE );
			}
		}

		return buf.toString().trim();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#isHeaderFile(java.lang.String)
	 */
	@Override
	public boolean isHeaderFile(String ext) {
		if (ext == null) {
			return false;
		}
		String[] exts = getAllDependencyExtensions();
		for (String dep : exts) {
			if (ext.equals(dep)) return true;
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#buildsFileType(java.lang.String)
	 */
	@Override
	public boolean buildsFileType(String extension) {
		return buildsFileType(extension, getProject());
	}

	public boolean buildsFileType(String extension, IProject project) {

		if (extension == null)  {
			return false;
		}
		IInputType it = getInputType(extension, project);
		if (it != null) {
			//  Decide whether we "build" this type of file
			//
			//  1.  If this is the primary input, yes
			if (it == getPrimaryInputType()) {
				return true;
			}
			//  2.  If the option attribute is specified, no
			if (it.getOptionId() != null && it.getOptionId().length() > 0) {
				return false;
			}
			//  3.  If the assignToOption attribute is specified, no
			if (it.getAssignToOptionId() != null && it.getAssignToOptionId().length() > 0) {
				return false;
			}
			//  Else, yes
			return true;
		}
		//  If no InputTypes, check the inputExtensions attribute
		if (!hasInputTypes()) {
			return getInputExtensionsAttribute().contains(extension);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#isInputFileType(java.lang.String)
	 */
	@Override
	public boolean isInputFileType(String extension) {
		if (extension == null)  {
			return false;
		}
		IInputType it = getInputType(extension);
		if (it != null) {
			return true;
		}
		//  If no InputTypes, check the inputExtensions attribute
		if (!hasInputTypes()) {
			return getInputExtensionsAttribute().contains(extension);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.build.managed.ITool#producesFileType(java.lang.String)
	 */
	@Override
	public boolean producesFileType(String extension) {
		if (extension == null)  {
			return false;
		}
		//  Check the output-types first
		if (getOutputType(extension) != null) {
			return true;
		}
		//  If there are no OutputTypes, check the attribute
		if (!hasOutputTypes()) {
			String[] exts = getOutputsAttribute();
			if (exts != null) {
				for (String ext : exts) {
					if (ext.equals(extension))
						return true;
				}
			}
		}
		return false;
	}



	/**
	 * @return the pathconverterElement
	 */
	public IConfigurationElement getPathconverterElement() {
		return pathconverterElement;
	}

	@Override
	public IOptionPathConverter getOptionPathConverter()  {
		// Use existing converter
		if (optionPathConverter != null) {
			return optionPathConverter ;
		}
		if (optionPathConverter==null) {
			// If there is not yet a optionPathConverter try to construct from configuration element
			IConfigurationElement element = getPathconverterElement();
			if (element != null) {
				try {
					if (element.getAttribute(ITool.OPTIONPATHCONVERTER) != null) {
						optionPathConverter = (IOptionPathConverter) element
								.createExecutableExtension(ITool.OPTIONPATHCONVERTER);
					}
				} catch (CoreException e) {
				}
			}
			if (optionPathConverter==null)  {
				// If there is still no optionPathConverter, ask superclass of this tool whether it has a converter
				if (getSuperClass() != null) {
					ITool superTool = getSuperClass();
					optionPathConverter = superTool.getOptionPathConverter();
				}
			}
			// If there is still no converter, ask the toolchain for a
			// global converter
			if ((optionPathConverter==null)&&(getParent() instanceof IResourceConfiguration))  {
				// The tool belongs to a resource configuration
				IResourceConfiguration resourceConfiguration = (IResourceConfiguration) getParent();
				IConfiguration configuration = resourceConfiguration.getParent();
				if (null!=configuration)  {
					IToolChain toolchain = configuration.getToolChain();
					optionPathConverter = toolchain.getOptionPathConverter();
				}
			}
			if ((optionPathConverter==null)&&(getParent() instanceof IToolChain)) {
				// The tool belongs to a toolchain
				IToolChain toolchain = (IToolChain) getParent();
				optionPathConverter = toolchain.getOptionPathConverter();
			}
		}

		pathconverterElement = null; // discard now that we've created one
		return optionPathConverter ;
	}


/*
 *  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.ITool#isExtensionElement()
	 */
	@Override
	public boolean isExtensionElement() {
		return isExtensionTool;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#isDirty()
	 */
	@Override
	public boolean isDirty() {
		// This shouldn't be called for an extension tool
 		if (isExtensionTool) return false;

		// If I need saving, just say yes
		if (isDirty) return true;

		// Check my children
		for (InputType type : getInputTypeList())
			if (type.isDirty())
				return true;
		for (OutputType type : getOutputTypeList())
			if (type.isDirty())
				return true;

		// Otherwise see if any options need saving
		if (super.isDirty())
			return true;

		return isDirty;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setDirty(boolean)
	 */
	@Override
	public void setDirty(boolean isDirty) {
		this.isDirty = isDirty;
		// Propagate "false" to options
		super.setDirty(isDirty);
		// Propagate "false" to the children
		if (!isDirty) {
			for (InputType type : getInputTypeList())
				type.setDirty(false);

			for (OutputType type : getOutputTypeList())
				type.setDirty(false);
		}
	}

	/* (non-Javadoc)
	 *  Resolve the element IDs to interface references
	 */
	@Override
	public void resolveReferences() {
		if (!resolved) {
			resolved = true;
			// Resolve superClass
			if (superClassId != null && superClassId.length() > 0) {
				setSuperClassInternal( ManagedBuildManager.getExtensionTool(superClassId) );
				if (getSuperClass() == null) {
					// Report error
					ManagedBuildManager.outputResolveError(
							"superClass",	//$NON-NLS-1$
							superClassId,
							"tool",	//$NON-NLS-1$
							getId());
				} else {
					//  All of our superclasses must be resolved in order to properly
					//  resolve options to option categories
					((Tool)getSuperClass()).resolveReferences();
				}
			}
			//  Resolve HoldsOptions
			super.resolveReferences();
			//  Call resolveReferences on our children
			for (InputType current : getInputTypeList())
				current.resolveReferences();

			for (OutputType current : getOutputTypeList())
				current.resolveReferences();
		}
	}

	/**
	 * Look for ${VALUE} in the command string
	 */
	public String evaluateCommand( String command, String values ) {
		final int DOLLAR_VALUE_LENGTH = 8;

	    if( command == null ) return values.trim();

	    String ret = command;
	    boolean found = false;
	    int start = 0;
	    int index;
	    int len;
	    while ((index = ret.indexOf( "${", start )) >= 0 &&	 //$NON-NLS-1$
	    	   (len = ret.length()) >= index + DOLLAR_VALUE_LENGTH) {
	    	start = index;
	    	index = index+2;
	    	int ch = ret.charAt(index);
	    	if ( ch == 'v' ||  ch == 'V' ) {
	    		index++;
	    		ch = ret.charAt(index);
		    	if ( ch == 'a' ||  ch == 'A' ) {
		    		index++;
		    		ch = ret.charAt(index);
			    	if ( ch == 'l' ||  ch == 'L' ) {
			    		index++;
			    		ch = ret.charAt(index);
				    	if ( ch == 'u' ||  ch == 'U' ) {
				    		index++;
				    		ch = ret.charAt(index);
					    	if ( ch == 'e' ||  ch == 'E' ) {
					    		index++;
					    		ch = ret.charAt(index);
						    	if ( ch == '}' ) {
						    		String temp = "";	//$NON-NLS-1$
						    		index++;
						    		found = true;
						    		if (start > 0) {
						    			temp = ret.substring(0, start);
						    		}
						    		temp = temp.concat(values.trim());
						    		if (len > index) {
						    			start = temp.length();
						    			ret = temp.concat(ret.substring(index));
						    			index = start;
						    		}
						    		else {
						    			ret = temp;
						    			break;
						    		}
						    	}
					    	}
				    	}
			    	}
		    	}
	    	}
	    	start = index;
	    }
	    if (found)
	    	return ret.trim();
    	return (new String(command + values)).trim();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getConvertToId()
	 */
	@Override
	public String getConvertToId() {
		if (convertToId == null) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return getSuperClass().getConvertToId();
			} else {
				return EMPTY_STRING;
			}
		}
		return convertToId;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setConvertToId(String)
	 */
	@Override
	public void setConvertToId(String convertToId) {
		if (convertToId == null && this.convertToId == null) return;
		if (convertToId == null || this.convertToId == null || !convertToId.equals(this.convertToId)) {
			this.convertToId = convertToId;
			setDirty(true);
		}
		return;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getVersionsSupported()
	 */
	@Override
	public String getVersionsSupported() {
		if (versionsSupported == null) {
			// If I have a superClass, ask it
			if (getSuperClass() != null) {
				return getSuperClass().getVersionsSupported();
			} else {
				return EMPTY_STRING;
			}
		}
		return versionsSupported;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#setVersionsSupported(String)
	 */
	@Override
	public void setVersionsSupported(String versionsSupported) {
		if (versionsSupported == null && this.versionsSupported == null) return;
		if (versionsSupported == null || this.versionsSupported == null || !versionsSupported.equals(this.versionsSupported)) {
			this.versionsSupported = versionsSupported;
			setDirty(true);
		}
		return;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.core.ITool#getEnvVarBuildPaths()
	 */
	@Override
	public IEnvVarBuildPath[] getEnvVarBuildPaths(){
		if(envVarBuildPathList != null){
			return envVarBuildPathList.toArray(
					new IEnvVarBuildPath[envVarBuildPathList.size()]);
		}
		else if(getSuperClass() != null)
			return getSuperClass().getEnvVarBuildPaths();
		return null;
	}

	private void addEnvVarBuildPath(IEnvVarBuildPath path){
		if(path == null)
			return;
		if(envVarBuildPathList == null)
			envVarBuildPathList = new ArrayList<IEnvVarBuildPath>();

		envVarBuildPathList.add(path);
	}

	/*
	 * This function checks for migration support for the tool, while
	 * loading. If migration support is needed, looks for the available
	 * converters and stores them.
	 */

	public void checkForMigrationSupport() {

		boolean isExists = false;

		if ( getSuperClass() == null) {
			// If 'getSuperClass()' is null, then there is no tool available in
			// plugin manifest file with the same 'id' & version.
			// Look for the 'versionsSupported' attribute
			String high = ManagedBuildManager.getExtensionToolMap().lastKey();

			SortedMap<String, ? extends ITool> subMap = null;
			if (superClassId.compareTo(high) <= 0) {
				subMap = ManagedBuildManager.getExtensionToolMap().subMap(
						superClassId, high + "\0"); //$NON-NLS-1$
			} else {
				// It means there are no entries in the map for the given id.
				// make the project is invalid

				// It means there are no entries in the map for the given id.
				// make the project is invalid
				// If the parent is a tool chain
				IToolChain parent = (IToolChain) getParent();
				IConfiguration parentConfig = parent.getParent();
				IManagedProject managedProject = parentConfig
						.getManagedProject();
				if (managedProject != null) {
					managedProject.setValid(false);
				}
				return;
			}

			// for each element in the 'subMap',
			// check the 'versionsSupported' attribute whether the given
			// tool version is supported

			String baseId = ManagedBuildManager
					.getIdFromIdAndVersion(superClassId);
			String version = ManagedBuildManager
					.getVersionFromIdAndVersion(superClassId);

			for (ITool toolElement : subMap.values()) {
				if (ManagedBuildManager.getIdFromIdAndVersion(
						toolElement.getId()).compareTo(baseId) > 0)
					break;

				// First check if both base ids are equal
				if (ManagedBuildManager.getIdFromIdAndVersion(
						toolElement.getId()).equals(baseId)) {

					// Check if 'versionsSupported' attribute is available'
					String versionsSupported = toolElement
							.getVersionsSupported();

					if ((versionsSupported != null)
							&& (!versionsSupported.equals(""))) { //$NON-NLS-1$
						String[] tmpVersions = versionsSupported.split(","); //$NON-NLS-1$

						for (String tmpVersion : tmpVersions) {
							if (new Version(version).equals(new Version(tmpVersion))) {
								// version is supported.
								// Do the automatic conversion without
								// prompting the user.
								// Get the supported version
								String supportedVersion = ManagedBuildManager
										.getVersionFromIdAndVersion(toolElement
												.getId());
								setId(ManagedBuildManager
										.getIdFromIdAndVersion(getId())
										+ "_" + supportedVersion); //$NON-NLS-1$

								// If control comes here means that superClass
								// is null.
								// So, set the superClass to this tool element
								setSuperClassInternal(toolElement);
								superClassId = getSuperClass().getId();
								isExists = true;
								break;
							}
						}
						if (isExists)
							break; // break the outer for loop if 'isExists' is
									// true
					}
				}
			}
		}

		if (getSuperClass() != null) {
			// If 'getSuperClass()' is not null, look for 'convertToId'
			// attribute in plugin
			// manifest file for this tool.
			String convertToId = getSuperClass().getConvertToId();
			if ((convertToId == null) || (convertToId.equals(""))) { //$NON-NLS-1$
				// It means there is no 'convertToId' attribute available and
				// the version is still actively
				// supported by the tool integrator. So do nothing, just return
				return;
			} else {
				// Incase the 'convertToId' attribute is available,
				// it means that Tool integrator currently does not support this
				// version of tool.
				// Look for the converters available for this tool version.

				getConverter(convertToId);
			}

		} else {
			// make the project is invalid
			//
			// It means there are no entries in the map for the given id.
			// make the project is invalid
			IToolChain parent = (IToolChain) getParent();
			IConfiguration parentConfig = parent.getParent();
			IManagedProject managedProject = parentConfig.getManagedProject();
			if (managedProject != null) {
				managedProject.setValid(false);
			}
		}
		return;
	}


	private void getConverter(String convertToId) {

		String fromId = null;
		String toId = null;

		// Get the Converter Extension Point
		IExtensionPoint extensionPoint = Platform.getExtensionRegistry()
				.getExtensionPoint("org.eclipse.cdt.managedbuilder.core", //$NON-NLS-1$
						"projectConverter"); //$NON-NLS-1$
		if (extensionPoint != null) {
			// Get the extensions
			IExtension[] extensions = extensionPoint.getExtensions();
			for (IExtension extension : extensions) {
				// Get the configuration elements of each extension
				IConfigurationElement[] configElements = extension
						.getConfigurationElements();
				for (IConfigurationElement element : configElements) {

					if (element.getName().equals("converter")) { //$NON-NLS-1$

						fromId = element.getAttribute("fromId"); //$NON-NLS-1$
						toId = element.getAttribute("toId"); //$NON-NLS-1$
						// Check whether the current converter can be used for
						// the selected tool

						if (fromId.equals(getSuperClass().getId())
								&& toId.equals(convertToId)) {
							// If it matches
							String mbsVersion = element
									.getAttribute("mbsVersion"); //$NON-NLS-1$
							Version currentMbsVersion = ManagedBuildManager
									.getBuildInfoVersion();

							// set the converter element based on the MbsVersion
							if (currentMbsVersion.compareTo(new Version(mbsVersion))>0) {
								previousMbsVersionConversionElement = element;
							} else {
								currentMbsVersionConversionElement = element;
							}
							return;
						}
					}
				}
			}
		}

		// If control comes here, it means 'Tool Integrator' specified
		// 'convertToId' attribute in toolchain definition file, but
		// has not provided any converter.
		// So, make the project is invalid

		// It means there are no entries in the map for the given id.
		// make the project is invalid
		IToolChain parent = (IToolChain) getParent();
		IConfiguration parentConfig = parent.getParent();
		IManagedProject managedProject = parentConfig.getManagedProject();
		if (managedProject != null) {
			managedProject.setValid(false);
		}
		return;
	}

	public IConfigurationElement getPreviousMbsVersionConversionElement() {
		return previousMbsVersionConversionElement;
	}

	public IConfigurationElement getCurrentMbsVersionConversionElement() {
		return currentMbsVersionConversionElement;
	}

	public IProject getProject() {
		IBuildObject toolParent = getParent();
		if (toolParent != null) {
			if (toolParent instanceof IToolChain) {
				IConfiguration config = ((IToolChain)toolParent).getParent();
				if (config == null) return null;
				return (IProject)config.getOwner();
			} else if (toolParent instanceof IResourceConfiguration) {
				return (IProject)((IResourceConfiguration)toolParent).getOwner();
			}
		}
		return null;
	}

	public String[] getContentTypeFileSpecs (IContentType type) {
		return getContentTypeFileSpecs(type, getProject());
	}

	public String[] getContentTypeFileSpecs (IContentType type, IProject project) {
		String[] globalSpecs = type.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
		IContentTypeSettings settings = null;
//		IProject project = getProject();
		if (project != null) {
			IScopeContext projectScope = new ProjectScope(project);
			try {
				settings = type.getSettings(projectScope);
			} catch (Exception e) {}
			if (settings != null) {
				String[] specs = settings.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
				if (specs.length > 0) {
					int total = globalSpecs.length + specs.length;
					String[] projSpecs = new String[total];
					int i=0;
					for (String spec : specs) {
						projSpecs[i] = spec;
						i++;
					}
					for (String spec : globalSpecs) {
						projSpecs[i] = spec;
						i++;
					}
					return projSpecs;
				}
			}
		}
		return globalSpecs;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.internal.core.HoldsOptions#needsRebuild()
	 */
	@Override
	public boolean needsRebuild() {
		if(rebuildState)
			return true;

		// Check my children
		for (InputType type : getInputTypeList())
			if (type.needsRebuild())
				return true;

		for (OutputType type : getOutputTypeList())
			if (type.needsRebuild())
				return true;

		return super.needsRebuild();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.internal.core.HoldsOptions#setRebuildState(boolean)
	 */
	@Override
	public void setRebuildState(boolean rebuild) {
		if(isExtensionElement() && rebuild)
			return;

		if(rebuildState != rebuild){
			rebuildState = rebuild;
			saveRebuildState();
		}

		if(!rebuild){
			super.setRebuildState(rebuild);

			if (!rebuild) {
				for (InputType type : getInputTypeList())
					type.setRebuildState(false);

				for (OutputType type : getOutputTypeList())
					type.setRebuildState(false);
			}
		}
	}

	private void saveRebuildState(){
		PropertyManager.getInstance().setProperty(this, REBUILD_STATE, Boolean.toString(rebuildState));
	}

	@Override
	public CLanguageData getCLanguageData(IInputType type) {
		initDataMap();
		return typeToDataMap.get(type);
	}

	private void initDataMap(){
		if(fDataMapInited)
			return;

		List<IInputType> types = getLanguageInputTypes();

		if(types != null){
			if(types.size() == 0){
				CLanguageData data = typeToDataMap.get(null);
				if(data == null){
					data = new BuildLanguageData(this, null);
					if(typeToDataMap.size() != 0){
						typeToDataMap.clear();
					}
					typeToDataMap.put(null, data);
				}

			} else {
				//create editable input types for lang datas first

				for(ListIterator<IInputType> iter = types.listIterator(); iter.hasNext();){
					IInputType type = iter.next();
					iter.set(getEditableInputType(type));
				}

				@SuppressWarnings("unchecked")
				Map<IInputType, CLanguageData> map = (Map<IInputType, CLanguageData>)typeToDataMap.clone();
				for(IInputType type : types){
					CLanguageData data = map.remove(type);
					if(data == null){
						data = new BuildLanguageData(this, type);
						typeToDataMap.put(type, data);
					}
				}

				if(map.size() > 0){
					for(IInputType it : map.keySet()){
						typeToDataMap.remove(it);
					}
				}
			}
		}
		fDataMapInited = true;
	}

	private boolean isLanguageInputType(IInputType type, boolean checkLangSettings){
		boolean supports = type.getLanguageId(this) != null
			|| typeContributesToScannerConfig((InputType)type);

		if(supports && checkLangSettings){
			supports = supportsLanguageSettings();
		}

		return supports;
	}

	public boolean supportsLanguageSettings(){
		IOption options[] = getOptions();
		boolean found = false;
		for (IOption option : options) {
			try {
				int type = option.getValueType();
				if(ManagedBuildManager.optionTypeToEntryKind(type) != 0){
					found = true;
					break;
				}
			} catch (BuildException e) {
			}
		}
		return found;
	}

	private List<IInputType> getLanguageInputTypes(){
		List<IInputType> list = new ArrayList<IInputType>();
		IInputType[] types = getInputTypes();
		for(IInputType t : types){
			InputType type = (InputType)t;
			if(isLanguageInputType(type, false))
				list.add(type);
		}
		if(list.size() == 0){
			if(types.length == 1){
				list.add(types[0]);
			} else {
				for(IInputType t : types){
					if(t.getPrimaryInput()){
						list.add(t);
						break;
					}
				}
			}
		}

		boolean found = supportsLanguageSettings();
		if(!found)
			return null;
		return list;
	}

	@Override
	public CLanguageData[] getCLanguageDatas() {
		initDataMap();
		return typeToDataMap.values().toArray(new BuildLanguageData[typeToDataMap.size()]);
	}

	@Override
	public IInputType getInputTypeForCLanguageData(CLanguageData data) {
		if(data instanceof BuildLanguageData)
			return ((BuildLanguageData)data).getInputType();
		return null;
	}

	@Override
	public IResourceInfo getParentResourceInfo() {
		if(parent instanceof IFileInfo)
			return (IResourceInfo)parent;
		else if(parent instanceof IToolChain)
			return ((IToolChain)parent).getParentFolderInfo();
		return null;
	}


	@Override
	public IInputType getEditableInputType(IInputType base) {
		if(base.getParent() == this)
			return base;

		IInputType extType = base;
		for(;extType != null && !extType.isExtensionElement();extType = extType.getSuperClass()) {
			// empty body
		}
		String id;
		if(extType != null){
			id = ManagedBuildManager.calculateChildId(extType.getId(), null);
		} else {
			id = ManagedBuildManager.calculateChildId(getId(), null);
		}
		InputType newType = (InputType)createInputType(base, id, base.getName(), false);
		IAdditionalInput addlInputs[] = base.getAdditionalInputs();
		for (IAdditionalInput addlInput : addlInputs) {
			newType.createAdditionalInput(addlInput);
		}
		return newType;
	}

	@Override
	public IOutputType getEditableOutputType(IOutputType base) {
		if(base.getParent() == this)
			return base;

		IOutputType extType = base;
		for(;extType != null && !extType.isExtensionElement();extType = extType.getSuperClass()){
			// empty body - loop to find extension
		}
		String id;
		if(extType != null){
			id = ManagedBuildManager.calculateChildId(extType.getId(), null);
		} else {
			id = ManagedBuildManager.calculateChildId(getId(), null);
		}
		IOutputType newType = createOutputType(base, id, base.getName(), false);
		return newType;
	}

	@Override
	public boolean supportsType(IBuildPropertyType type) {
		return supportsType(type.getId());
	}

	@Override
	public boolean supportsType(String type) {
		boolean suports = false;

		SupportedProperties props = findSupportedProperties();
		if(props != null) {
			suports = props.supportsType(type);
		} else {
			BooleanExpressionApplicabilityCalculator calc = getBooleanExpressionCalculator();
			if(calc != null){
				suports = calc.referesProperty(type);
			}

			if(!suports)
				suports = super.supportsType(type);
		}
		return suports;
	}

	@Override
	public boolean supportsValue(String type, String value){
		boolean suports = false;
		SupportedProperties props = findSupportedProperties();
		if(props != null) {
			suports = props.supportsValue(type, value);
		} else {
			BooleanExpressionApplicabilityCalculator calc = getBooleanExpressionCalculator();
			if(calc != null){
				suports = calc.referesPropertyValue(type, value);
			}

			if(!suports)
				suports = super.supportsValue(type, value);
		}
		return suports;
	}

	@Override
	public boolean supportsValue(IBuildPropertyType type,
			IBuildPropertyValue value) {
		return supportsValue(type.getId(), value.getId());
	}

	@Override
	public void propertiesChanged() {
		if(isExtensionTool)
			return;

		BooleanExpressionApplicabilityCalculator calculator = getBooleanExpressionCalculator();
		if(calculator != null)
			calculator.adjustTool(getParentResourceInfo(), this, false);

		super.propertiesChanged();
	}

	public BooleanExpressionApplicabilityCalculator getBooleanExpressionCalculator(){
		if(booleanExpressionCalculator == null){
			if(superClass != null){
				return ((Tool)superClass).getBooleanExpressionCalculator();
			}
		}
		return booleanExpressionCalculator;
	}

	@Override
	public boolean isEnabled() {
		return isEnabled(getParentResourceInfo());
	}

	public boolean isEnabled(IResourceInfo rcInfo) {

		BooleanExpressionApplicabilityCalculator calc = getBooleanExpressionCalculator();
		if(calc == null)
			return true;

		return calc.isToolUsedInCommandLine(rcInfo, this);
	}

	@Override
	public boolean matches(ITool tool){
		if(tool == this)
			return true;

		ITool rT = ManagedBuildManager.getRealTool(this);
		if(rT == null)
			return false;

		return rT == ManagedBuildManager.getRealTool(tool);
	}

/*	public SupportedProperties getSupportedProperties(){
		Map map = findSupportedProperties();
		if(map != null)
			return new HashMap(map);
		return null;
	}
*/
	private SupportedProperties findSupportedProperties(){
		if(supportedProperties == null){
			if(superClass != null){
				return ((Tool)superClass).findSupportedProperties();
			}
		}
		return supportedProperties;
	}

	@Override
	public boolean supportsBuild(boolean managed){
		if(supportsManagedBuild == null){
			if(superClass != null){
				return ((Tool)superClass).supportsBuild(managed);
			}
			return true;
		}
		return supportsManagedBuild.booleanValue();
	}

	@Override
	public MatchKey<Tool> getMatchKey() {
		if(isAbstract())
			return null;
		if(!isExtensionTool)
			return null;
		return new MatchKey<Tool>(this);
	}

	@Override
	public void setIdenticalList(List<Tool> list) {
		identicalList = list;
	}

	public String getNameAndVersion(){
		String name = getName();
		String version = ManagedBuildManager.getVersionFromIdAndVersion(getId());
		if(version != null && version.length() != 0){
			return new StringBuilder().append(name).append(" (").append(version).append("").toString(); //$NON-NLS-1$ //$NON-NLS-2$
		}
		return name;
	}

	public IConfigurationElement getConverterModificationElement(ITool toTool){
		Map<String, IConfigurationElement> map = ManagedBuildManager.getConversionElements(this);
		IConfigurationElement element = null;
		if(!map.isEmpty()){
			for(IConfigurationElement el : map.values()){
				String toId = el.getAttribute("toId"); //$NON-NLS-1$
				ITool to = toTool;
				if(toId != null){
					for(;to != null; to = to.getSuperClass()){
						if(toId.equals(to.getId()))
							break;
					}
				}

				if(to != null){
					element = el;
					break;
				}
			}
		}

		return element;
	}

	void updateParent(IBuildObject parent){
		this.parent = parent;
	}

	void updateParentResourceInfo(IResourceInfo rcInfo){
		if(rcInfo instanceof IFileInfo)
			this.parent = rcInfo;
		else
			this.parent = ((IFolderInfo)rcInfo).getToolChain();
	}

	@Override
	public List<Tool> getIdenticalList() {
		return identicalList;
	}

	@Override
	public String[] getRequiredTypeIds() {
		SupportedProperties props = findSupportedProperties();
		String[] required = null;
		if(props != null) {
			required = props.getRequiredTypeIds();
		} else {
			required = super.getRequiredTypeIds();
		}
		return required;
	}

	@Override
	public String[] getSupportedTypeIds() {
		SupportedProperties props = findSupportedProperties();
		String[] supported = null;
		if(props != null) {
			supported = props.getSupportedTypeIds();
		} else {
			BooleanExpressionApplicabilityCalculator calc = getBooleanExpressionCalculator();
			List<String> list = new ArrayList<String>();
			if(calc != null){
				list.addAll(Arrays.asList(calc.getReferencedPropertyIds()));
			}

			list.addAll(Arrays.asList(super.getSupportedTypeIds()));
			supported = list.toArray(new String[list.size()]);
		}
		return supported;
	}

	@Override
	public String[] getSupportedValueIds(String typeId) {
		SupportedProperties props = findSupportedProperties();
		String[] supported = null;
		if(props != null) {
			supported = props.getSupportedValueIds(typeId);
		} else {
			BooleanExpressionApplicabilityCalculator calc = getBooleanExpressionCalculator();
			List<String> list = new ArrayList<String>();
			if(calc != null){
				list.addAll(Arrays.asList(calc.getReferencedValueIds(typeId)));
			}

			list.addAll(Arrays.asList(super.getSupportedValueIds(typeId)));
			supported = list.toArray(new String[list.size()]);
		}
		return supported;
	}

	@Override
	public boolean requiresType(String typeId) {
		SupportedProperties props = findSupportedProperties();
		boolean required;
		if(props != null) {
			required = props.requiresType(typeId);
		} else {
			required = super.requiresType(typeId);
		}
		return required;
	}

	public void resetErrorParsers(){
		errorParserIds = null;
	}

	void removeErrorParsers(Set<String> set){
		Set<String> oldSet = contributeErrorParsers(null);
		if(oldSet == null)
			oldSet = new HashSet<String>();

		oldSet.removeAll(set);
		setErrorParserList(oldSet.toArray(new String[oldSet.size()]));
	}

	public void setErrorParserList(String[] ids) {
		if(ids == null){
			errorParserIds = null;
		} else if(ids.length == 0){
			errorParserIds = EMPTY_STRING;
		} else {
			StringBuilder buf = new StringBuilder();
			buf.append(ids[0]);
			for(int i = 1; i < ids.length; i++){
				buf.append(";").append(ids[i]); //$NON-NLS-1$
			}
			errorParserIds = buf.toString();
		}
	}

	@Override
	public boolean isSystemObject() {
		if(isTest)
			return true;

		if(getConvertToId().length() != 0)
			return true;

		IBuildObject bo = getParent();
		if(bo instanceof IToolChain)
			return ((IToolChain)bo).isSystemObject();
		return false;
	}

	@Override
	public String getUniqueRealName() {
		String name = getName();
		if(name == null){
			name = getId();
		} else {
			String version = ManagedBuildManager.getVersionFromIdAndVersion(getId());
			if(version != null){
			StringBuilder buf = new StringBuilder();
			buf.append(name);
			buf.append(" (v").append(version).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
			name = buf.toString();
			}
		}
		return name;
	}

	private boolean typeContributesToScannerConfig(InputType inType){
//		InputType inType = (InputType)type;

		if(inType.getDiscoveryProfileId(this) != null)
			return true;

		return false;
	}

	public boolean hasScannerConfigSettings(IInputType type){
		if(type == null){
			boolean has = hasScannerConfigSettings();
			if (has)
				return has;
			ITool superClass = getSuperClass();
			if (superClass!=null && superClass instanceof Tool)
				return ((Tool)superClass).hasScannerConfigSettings(type);
			return false;
		}
		return ((InputType)type).hasScannerConfigSettings();
	}

	private boolean hasScannerConfigSettings(){

		if(getDiscoveryProfileIdAttribute() != null)
			return true;

		return false;
	}

	public PathInfoCache setDiscoveredPathInfo(IInputType type, PathInfoCache info){
		return discoveredInfoMap.put(getTypeKey(type), info);
	}

	public PathInfoCache getDiscoveredPathInfo(IInputType type){
		return discoveredInfoMap.get(getTypeKey(type));
	}

	public PathInfoCache clearDiscoveredPathInfo(IInputType type){
		return discoveredInfoMap.remove(getTypeKey(type));
	}

	public void clearAllDiscoveredPathInfo(){
		discoveredInfoMap.clear();
	}

	public void clearAllDiscoveredInfo(){
		discoveredInfoMap.clear();
	}

	private String getTypeKey(IInputType type){
		if(type != null)
			return type.getId();
		return null;
	}

	public String getDiscoveryProfileIdAttribute(){
		if(scannerConfigDiscoveryProfileId == null && superClass != null)
			return ((Tool)superClass).getDiscoveryProfileIdAttribute();
		return scannerConfigDiscoveryProfileId;
	}

	private IToolChain getToolChain(){
		IBuildObject bo = getParent();
		IToolChain tCh = null;
		if(bo instanceof IToolChain){
			tCh = ((IToolChain)bo);
		} else if(bo instanceof IFileInfo){
			tCh = ((ResourceConfiguration)bo).getBaseToolChain();
		}
		return tCh;
	}

	public String getDiscoveryProfileId(){
		String id = getDiscoveryProfileIdAttribute();
		if(id == null){
			IToolChain tc = getToolChain();
			if(tc != null)
				id = tc.getScannerConfigDiscoveryProfileId();
		}
		return id;
	}

	public boolean hasCustomSettings(Tool tool){
		if(superClass == null)
			return true;

		ITool realTool = ManagedBuildManager.getRealTool(this);
		ITool otherRealTool = ManagedBuildManager.getRealTool(tool);
		if(realTool != otherRealTool)
			return true;

		if(hasCustomSettings())
			return true;

		if(inputTypeList != null && inputTypeList.size() != 0){
			for(InputType inType : inputTypeList){
				if(inType.hasCustomSettings())
					return true;
			}
		}

		if(outputTypeList != null && outputTypeList.size() != 0){
			for(OutputType outType : outputTypeList){
				if(outType.hasCustomSettings())
					return true;
			}
		}
		Tool superTool = (Tool)superClass;

		if(command != null && !command.equals(superTool.getToolCommand()))
			return true;

		if(errorParserIds != null && !errorParserIds.equals(superTool.getErrorParserIds()))
			return true;

		if(commandLinePattern != null && !commandLinePattern.equals(superTool.getCommandLinePattern()))
			return true;

		if(customBuildStep != null && customBuildStep.booleanValue() != superTool.getCustomBuildStep())
			return true;

		if(announcement != null && !announcement.equals(superTool.getAnnouncement()))
			return true;

		if(discoveredInfoMap != null && discoveredInfoMap.size() != 0)
			return true;

		if (isAnyOptionModified(this, tool))
			return true;

		return false;
	}

	private boolean isAnyOptionModified(ITool t1, ITool t2) {
		for (IOption op1 : t1.getOptions()) {
			for (IOption op2 : t2.getOptions()) {
				// find matching option
				try {
					if (op1.getValueType() == op2.getValueType() &&
						op1.getName().equals(op2.getName())) {
						Object ob1 = op1.getValue();
						Object ob2 = op2.getValue();
						if (ob1 == null && ob2 == null)
							break;
						// values are different ?
						if ((ob1 == null || ob2 == null) ||
						   !(ob1.equals(ob2) ))
							return true;
						else
							break;
					}
				} catch (BuildException e) {
					return true; // unprobable
				}
			}
		}
		return false;
	}

	public IOption[] getOptionsOfType(int type){
		List<IOption> list = new ArrayList<IOption>();
		for(IOption op : getOptions()){
			try {
				if(op.getValueType() == type)
					list.add(op);
			} catch (BuildException e) {
				ManagedBuilderCorePlugin.log(e);
			}
		}
		return list.toArray(new Option[list.size()]);
	}

	public void filterValues(int type, List<String> values){
		if(values.size() == 0)
			return;

		int opType = Option.getOppositeType(type);
		if(opType != 0){
			Set<Object> filterSet = new HashSet<Object>();
			for(IOption op : getOptionsOfType(opType)){
				filterSet.addAll((List<Object>)op.getValue());
			}

			if(filterSet.size() != 0){
				for (Iterator<String> iterator = values.iterator(); iterator.hasNext();) {
					String oVal = iterator.next();
					if(type == IOption.PREPROCESSOR_SYMBOLS){
						String[] nameVal = BuildEntryStorage.macroNameValueFromValue(oVal);
						oVal = nameVal[0];
					}
					if(filterSet.contains(oVal))
						iterator.remove();
				}
			}
		}
	}

	private int getSuperClassNum(){
		int num = 0;
		for(ITool superTool = getSuperClass(); superTool != null; superTool = superTool.getSuperClass()){
			num++;
		}
		return num;
	}

	@Override
	public int compareTo(Tool other) {
		if(other.isSystemObject() != isSystemObject())
			return isSystemObject() ? 1 : -1;

		return getSuperClassNum() - other.getSuperClassNum();
	}

	@Override
	public IRealBuildObjectAssociation getExtensionObject() {
		return (IRealBuildObjectAssociation)ManagedBuildManager.getExtensionTool(this);
	}

	@Override
	public IRealBuildObjectAssociation[] getIdenticBuildObjects() {
		return (IRealBuildObjectAssociation[])ManagedBuildManager.findIdenticalTools(this);
	}

	@Override
	public IRealBuildObjectAssociation getRealBuildObject() {
		return (IRealBuildObjectAssociation)ManagedBuildManager.getRealTool(this);
	}

	@Override
	public IRealBuildObjectAssociation getSuperClassObject() {
		return (IRealBuildObjectAssociation)getSuperClass();
	}

	@Override
	public final int getType() {
		return OBJECT_TOOL;
	}

	@Override
	public boolean isRealBuildObject() {
		return getRealBuildObject() == this;
	}

	@Override
	public boolean isExtensionBuildObject() {
		return isExtensionElement();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.cdt.managedbuilder.core.IOptionCategory#getApplicabilityCalculator()
	 */
	@Override
	public IOptionCategoryApplicability getApplicabilityCalculator() {
		// Tool does not have any ApplicabilityCalculator.
		return null;
	}
}
