1 /******************************************************************************* 2 * Copyright (c) 2000, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * James Blackburn (Broadcom Corp.) - Custom trigger builder #equals 14 * Broadcom Corporation - ongoing development 15 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 16 *******************************************************************************/ 17 package org.eclipse.core.internal.events; 18 19 import java.util.*; 20 import org.eclipse.core.internal.resources.ModelObject; 21 import org.eclipse.core.resources.*; 22 import org.eclipse.core.runtime.*; 23 24 /** 25 * The concrete implementation of <tt>ICommand</tt>. This object 26 * stores information about a particular type of builder. 27 * 28 * If the builder has been instantiated, a reference to the builder is held. 29 * If the builder supports multiple build configurations, a reference to the 30 * builder for each configuration is held. 31 */ 32 public class BuildCommand extends ModelObject implements ICommand { 33 /** 34 * Internal flag masks for different build triggers. 35 */ 36 private static final int MASK_AUTO = 0x01; 37 private static final int MASK_INCREMENTAL = 0x02; 38 private static final int MASK_FULL = 0x04; 39 private static final int MASK_CLEAN = 0x08; 40 41 /** 42 * Flag bit indicating if this build command is configurable 43 */ 44 private static final int MASK_CONFIGURABLE = 0x10; 45 46 /** 47 * Flag bit indicating if the configurable bit has been loaded from 48 * the builder extension declaration in XML yet. 49 */ 50 private static final int MASK_CONFIG_COMPUTED = 0x20; 51 52 private static final int ALL_TRIGGERS = MASK_AUTO | MASK_CLEAN | MASK_FULL | MASK_INCREMENTAL; 53 54 protected HashMap<String, String> arguments = new HashMap<>(0); 55 56 /** Have we checked the supports configurations flag */ 57 private boolean supportsConfigurationsCalculated; 58 /** Does this builder support configurations */ 59 private boolean supportsConfigurations; 60 /** 61 * The builder instance for this command. Null if the builder has 62 * not yet been instantiated. 63 */ 64 private IncrementalProjectBuilder builder; 65 /** 66 * The builders for this command if the builder supports multiple configurations 67 */ 68 private HashMap<IBuildConfiguration, IncrementalProjectBuilder> builders; 69 70 /** 71 * The triggers that this builder will respond to. Since build triggers are not 72 * bit-maskable, we use internal bit masks to represent each 73 * trigger (MASK_* constants). By default, a command responds to all 74 * build triggers. 75 */ 76 private int triggers = ALL_TRIGGERS; 77 78 /** 79 * Lock used to synchronize access to {@link #builder} and {@link #builders}. 80 */ 81 private final Object builderLock = new Object(); 82 83 /** 84 * Returns the trigger bit mask for the given trigger constant. 85 */ maskForTrigger(int trigger)86 private static int maskForTrigger(int trigger) { 87 switch (trigger) { 88 case IncrementalProjectBuilder.AUTO_BUILD : 89 return MASK_AUTO; 90 case IncrementalProjectBuilder.INCREMENTAL_BUILD : 91 return MASK_INCREMENTAL; 92 case IncrementalProjectBuilder.FULL_BUILD : 93 return MASK_FULL; 94 case IncrementalProjectBuilder.CLEAN_BUILD : 95 return MASK_CLEAN; 96 } 97 return 0; 98 } 99 BuildCommand()100 public BuildCommand() { 101 super(""); //$NON-NLS-1$ 102 } 103 104 @Override clone()105 public Object clone() { 106 BuildCommand result = null; 107 result = (BuildCommand) super.clone(); 108 if (result == null) 109 return null; 110 result.setArguments(getArguments()); 111 //don't let references to builder instances leak out because they reference trees 112 result.setBuilders(null); 113 return result; 114 } 115 116 /** 117 * Computes whether this build command allows configuration of its 118 * triggers, based on information in the builder extension declaration. 119 */ computeIsConfigurable()120 private void computeIsConfigurable() { 121 triggers |= MASK_CONFIG_COMPUTED; 122 IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, name); 123 if (extension != null) { 124 IConfigurationElement[] configs = extension.getConfigurationElements(); 125 if (configs.length != 0) { 126 String value = configs[0].getAttribute("isConfigurable"); //$NON-NLS-1$ 127 setConfigurable(value != null && value.equalsIgnoreCase(Boolean.TRUE.toString())); 128 } 129 } 130 } 131 132 @Override equals(Object object)133 public boolean equals(Object object) { 134 if (this == object) 135 return true; 136 if (!(object instanceof BuildCommand)) 137 return false; 138 BuildCommand command = (BuildCommand) object; 139 // equal if same builder name, arguments, and triggers 140 return getBuilderName().equals(command.getBuilderName()) && getArguments(false).equals(command.getArguments(false)) && (triggers & ALL_TRIGGERS) == (command.triggers & ALL_TRIGGERS); 141 } 142 143 @Override getArguments()144 public Map<String, String> getArguments() { 145 return getArguments(true); 146 } 147 148 @SuppressWarnings({"unchecked"}) getArguments(boolean makeCopy)149 public Map<String, String> getArguments(boolean makeCopy) { 150 return arguments == null ? null : (makeCopy ? (Map<String, String>) arguments.clone() : arguments); 151 } 152 153 /** 154 * @return A copy of the internal map {@link IBuildConfiguration} -> {@link IncrementalProjectBuilder} if 155 * this build command supports multiple configurations. Otherwise return the {@link IncrementalProjectBuilder} 156 * associated with this build command. 157 */ getBuilders()158 public Object getBuilders() { 159 synchronized (builderLock) { 160 if (supportsConfigs()) { 161 return builders == null ? null : new HashMap<>(builders); 162 } 163 return builder; 164 } 165 } 166 167 /** 168 * Return the {@link IncrementalProjectBuilder} for the 169 * {@link IBuildConfiguration} If this builder is configuration agnostic, the 170 * same {@link IncrementalProjectBuilder} is returned for all configurations. 171 * 172 * @param config the config to get a builder for 173 * @return {@link IncrementalProjectBuilder} corresponding to config 174 */ getBuilder(IBuildConfiguration config)175 public IncrementalProjectBuilder getBuilder(IBuildConfiguration config) { 176 synchronized (builderLock) { 177 if (builders != null && supportsConfigs()) 178 return builders.get(config); 179 return builder; 180 } 181 } 182 183 @Override getBuilderName()184 public String getBuilderName() { 185 return getName(); 186 } 187 188 @Override hashCode()189 public int hashCode() { 190 // hash on name and trigger 191 return 37 * getName().hashCode() + (ALL_TRIGGERS & triggers); 192 } 193 194 @Override isBuilding(int trigger)195 public boolean isBuilding(int trigger) { 196 return (triggers & maskForTrigger(trigger)) != 0; 197 } 198 199 @Override isConfigurable()200 public boolean isConfigurable() { 201 if ((triggers & MASK_CONFIG_COMPUTED) == 0) 202 computeIsConfigurable(); 203 return (triggers & MASK_CONFIGURABLE) != 0; 204 } 205 supportsConfigs()206 public boolean supportsConfigs() { 207 if (!supportsConfigurationsCalculated) { 208 IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, name); 209 if (extension != null) { 210 IConfigurationElement[] configs = extension.getConfigurationElements(); 211 if (configs.length != 0) { 212 String value = configs[0].getAttribute("supportsConfigurations"); //$NON-NLS-1$ 213 supportsConfigurations = (value != null && value.equalsIgnoreCase(Boolean.TRUE.toString())); 214 } 215 } 216 supportsConfigurationsCalculated = true; 217 } 218 return supportsConfigurations; 219 } 220 221 @Override setArguments(Map<String, String> value)222 public void setArguments(Map<String, String> value) { 223 // copy parameter for safety's sake 224 arguments = value == null ? null : new HashMap<>(value); 225 } 226 227 /** 228 * Set the IncrementalProjectBuilder(s) for this command 229 * 230 * @param value a single {@link IncrementalProjectBuilder} or a {@link Map} of 231 * {@link IncrementalProjectBuilder} indexed by 232 * {@link IBuildConfiguration} 233 */ 234 @SuppressWarnings("unchecked") setBuilders(Object value)235 public void setBuilders(Object value) { 236 synchronized (builderLock) { 237 if (value == null) { 238 builder = null; 239 builders = null; 240 } else { 241 if (value instanceof IncrementalProjectBuilder) 242 builder = (IncrementalProjectBuilder) value; 243 else 244 builders = new HashMap<>((Map<IBuildConfiguration, IncrementalProjectBuilder>) value); 245 } 246 } 247 } 248 249 /** 250 * Add an IncrementalProjectBuilder for the given configuration. 251 * 252 * For builders which don't respond to multiple configurations, there's only one builder 253 * instance. 254 * 255 * Does nothing if a builder was already added for the specified configuration, 256 * or if a builder was added and this builder does not support multiple configurations. 257 * 258 * @param config 259 * @param newBuilder 260 */ addBuilder(IBuildConfiguration config, IncrementalProjectBuilder newBuilder)261 public void addBuilder(IBuildConfiguration config, IncrementalProjectBuilder newBuilder) { 262 synchronized (builderLock) { 263 // Builder shouldn't already exist in this build command 264 IncrementalProjectBuilder configBuilder = builders == null ? null : builders.get(config); 265 if (configBuilder == null && builder == null) { 266 if (supportsConfigs()) { 267 if (builders == null) 268 builders = new HashMap<>(1); 269 builders.put(config, newBuilder); 270 } else 271 builder = newBuilder; 272 } 273 } 274 } 275 276 @Override setBuilderName(String value)277 public void setBuilderName(String value) { 278 //don't allow builder name to be null 279 setName(value == null ? "" : value); //$NON-NLS-1$ 280 } 281 282 @Override setBuilding(int trigger, boolean value)283 public void setBuilding(int trigger, boolean value) { 284 if (!isConfigurable()) 285 return; 286 if (value) 287 triggers |= maskForTrigger(trigger); 288 else 289 triggers &= ~maskForTrigger(trigger); 290 } 291 292 /** 293 * Sets whether this build command allows its build triggers to be configured. 294 * This value should only be set when the builder extension declaration is 295 * read from the registry, or when a build command is read from the project 296 * description file on disk. The value is not otherwise mutable. 297 */ setConfigurable(boolean value)298 public void setConfigurable(boolean value) { 299 triggers |= MASK_CONFIG_COMPUTED; 300 if (value) 301 triggers |= MASK_CONFIGURABLE; 302 else 303 triggers = ALL_TRIGGERS; 304 } 305 306 /** 307 * For debugging purposes only 308 */ 309 @Override toString()310 public String toString() { 311 return "BuildCommand(" + getName() + ")";//$NON-NLS-1$ //$NON-NLS-2$ 312 } 313 } 314