blob: a20f4639cc950ae833231a808eb3f3bf10c73ad0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* James Blackburn (Broadcom Corp.) - Custom trigger builder #equals
* Broadcom Corporation - ongoing development
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427
*******************************************************************************/
package org.eclipse.core.internal.events;
import java.util.*;
import org.eclipse.core.internal.resources.ModelObject;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
/**
* The concrete implementation of <tt>ICommand</tt>. This object
* stores information about a particular type of builder.
*
* If the builder has been instantiated, a reference to the builder is held.
* If the builder supports multiple build configurations, a reference to the
* builder for each configuration is held.
*/
public class BuildCommand extends ModelObject implements ICommand {
/**
* Internal flag masks for different build triggers.
*/
private static final int MASK_AUTO = 0x01;
private static final int MASK_INCREMENTAL = 0x02;
private static final int MASK_FULL = 0x04;
private static final int MASK_CLEAN = 0x08;
/**
* Flag bit indicating if this build command is configurable
*/
private static final int MASK_CONFIGURABLE = 0x10;
/**
* Flag bit indicating if the configurable bit has been loaded from
* the builder extension declaration in XML yet.
*/
private static final int MASK_CONFIG_COMPUTED = 0x20;
private static final int ALL_TRIGGERS = MASK_AUTO | MASK_CLEAN | MASK_FULL | MASK_INCREMENTAL;
protected HashMap<String, String> arguments = new HashMap<>(0);
/** Have we checked the supports configurations flag */
private boolean supportsConfigurationsCalculated;
/** Does this builder support configurations */
private boolean supportsConfigurations;
/**
* The builder instance for this command. Null if the builder has
* not yet been instantiated.
*/
private IncrementalProjectBuilder builder;
/**
* The builders for this command if the builder supports multiple configurations
*/
private HashMap<IBuildConfiguration, IncrementalProjectBuilder> builders;
/**
* The triggers that this builder will respond to. Since build triggers are not
* bit-maskable, we use internal bit masks to represent each
* trigger (MASK_* constants). By default, a command responds to all
* build triggers.
*/
private int triggers = ALL_TRIGGERS;
/**
* Lock used to synchronize access to {@link #builder} and {@link #builders}.
*/
private final Object builderLock = new Object();
/**
* Returns the trigger bit mask for the given trigger constant.
*/
private static int maskForTrigger(int trigger) {
switch (trigger) {
case IncrementalProjectBuilder.AUTO_BUILD :
return MASK_AUTO;
case IncrementalProjectBuilder.INCREMENTAL_BUILD :
return MASK_INCREMENTAL;
case IncrementalProjectBuilder.FULL_BUILD :
return MASK_FULL;
case IncrementalProjectBuilder.CLEAN_BUILD :
return MASK_CLEAN;
}
return 0;
}
public BuildCommand() {
super(""); //$NON-NLS-1$
}
@Override
public Object clone() {
BuildCommand result = null;
result = (BuildCommand) super.clone();
if (result == null)
return null;
result.setArguments(getArguments());
//don't let references to builder instances leak out because they reference trees
result.setBuilders(null);
return result;
}
/**
* Computes whether this build command allows configuration of its
* triggers, based on information in the builder extension declaration.
*/
private void computeIsConfigurable() {
triggers |= MASK_CONFIG_COMPUTED;
IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, name);
if (extension != null) {
IConfigurationElement[] configs = extension.getConfigurationElements();
if (configs.length != 0) {
String value = configs[0].getAttribute("isConfigurable"); //$NON-NLS-1$
setConfigurable(value != null && value.equalsIgnoreCase(Boolean.TRUE.toString()));
}
}
}
@Override
public boolean equals(Object object) {
if (this == object)
return true;
if (!(object instanceof BuildCommand))
return false;
BuildCommand command = (BuildCommand) object;
// equal if same builder name, arguments, and triggers
return getBuilderName().equals(command.getBuilderName()) && getArguments(false).equals(command.getArguments(false)) && (triggers & ALL_TRIGGERS) == (command.triggers & ALL_TRIGGERS);
}
@Override
public Map<String, String> getArguments() {
return getArguments(true);
}
@SuppressWarnings({"unchecked"})
public Map<String, String> getArguments(boolean makeCopy) {
return arguments == null ? null : (makeCopy ? (Map<String, String>) arguments.clone() : arguments);
}
/**
* @return A copy of the internal map {@link IBuildConfiguration} -&gt; {@link IncrementalProjectBuilder} if
* this build command supports multiple configurations. Otherwise return the {@link IncrementalProjectBuilder}
* associated with this build command.
*/
public Object getBuilders() {
synchronized (builderLock) {
if (supportsConfigs()) {
return builders == null ? null : new HashMap<>(builders);
}
return builder;
}
}
/**
* Return the {@link IncrementalProjectBuilder} for the {@link IBuildConfiguration}
* If this builder is configuration agnostic, the same {@link IncrementalProjectBuilder} is
* returned for all configurations.
* @param config
* @return {@link IncrementalProjectBuilder} corresponding to config
*/
public IncrementalProjectBuilder getBuilder(IBuildConfiguration config) {
synchronized (builderLock) {
if (builders != null && supportsConfigs())
return builders.get(config);
return builder;
}
}
@Override
public String getBuilderName() {
return getName();
}
@Override
public int hashCode() {
// hash on name and trigger
return 37 * getName().hashCode() + (ALL_TRIGGERS & triggers);
}
@Override
public boolean isBuilding(int trigger) {
return (triggers & maskForTrigger(trigger)) != 0;
}
@Override
public boolean isConfigurable() {
if ((triggers & MASK_CONFIG_COMPUTED) == 0)
computeIsConfigurable();
return (triggers & MASK_CONFIGURABLE) != 0;
}
public boolean supportsConfigs() {
if (!supportsConfigurationsCalculated) {
IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, name);
if (extension != null) {
IConfigurationElement[] configs = extension.getConfigurationElements();
if (configs.length != 0) {
String value = configs[0].getAttribute("supportsConfigurations"); //$NON-NLS-1$
supportsConfigurations = (value != null && value.equalsIgnoreCase(Boolean.TRUE.toString()));
}
}
supportsConfigurationsCalculated = true;
}
return supportsConfigurations;
}
@Override
public void setArguments(Map<String, String> value) {
// copy parameter for safety's sake
arguments = value == null ? null : new HashMap<>(value);
}
/**
* Set the IncrementalProjectBuilder(s) for this command
* @param value
*/
@SuppressWarnings("unchecked")
public void setBuilders(Object value) {
synchronized (builderLock) {
if (value == null) {
builder = null;
builders = null;
} else {
if (value instanceof IncrementalProjectBuilder)
builder = (IncrementalProjectBuilder) value;
else
builders = new HashMap<>((Map<IBuildConfiguration, IncrementalProjectBuilder>) value);
}
}
}
/**
* Add an IncrementalProjectBuilder for the given configuration.
*
* For builders which don't respond to multiple configurations, there's only one builder
* instance.
*
* Does nothing if a builder was already added for the specified configuration,
* or if a builder was added and this builder does not support multiple configurations.
*
* @param config
* @param newBuilder
*/
public void addBuilder(IBuildConfiguration config, IncrementalProjectBuilder newBuilder) {
synchronized (builderLock) {
// Builder shouldn't already exist in this build command
IncrementalProjectBuilder configBuilder = builders == null ? null : builders.get(config);
if (configBuilder == null && builder == null) {
if (supportsConfigs()) {
if (builders == null)
builders = new HashMap<>(1);
builders.put(config, newBuilder);
} else
builder = newBuilder;
}
}
}
@Override
public void setBuilderName(String value) {
//don't allow builder name to be null
setName(value == null ? "" : value); //$NON-NLS-1$
}
@Override
public void setBuilding(int trigger, boolean value) {
if (!isConfigurable())
return;
if (value)
triggers |= maskForTrigger(trigger);
else
triggers &= ~maskForTrigger(trigger);
}
/**
* Sets whether this build command allows its build triggers to be configured.
* This value should only be set when the builder extension declaration is
* read from the registry, or when a build command is read from the project
* description file on disk. The value is not otherwise mutable.
*/
public void setConfigurable(boolean value) {
triggers |= MASK_CONFIG_COMPUTED;
if (value)
triggers |= MASK_CONFIGURABLE;
else
triggers = ALL_TRIGGERS;
}
/**
* For debugging purposes only
*/
@Override
public String toString() {
return "BuildCommand(" + getName() + ")";//$NON-NLS-1$ //$NON-NLS-2$
}
}