/*******************************************************************************
* Copyright (c) 2011 Wind River Systems, Inc. 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.te.runtime.stepper.extensions;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tm.te.runtime.interfaces.extensions.IExecutableExtension;
import org.eclipse.tm.te.runtime.stepper.StepperManager;
import org.eclipse.tm.te.runtime.stepper.activator.CoreBundleActivator;
import org.eclipse.tm.te.runtime.stepper.interfaces.IContextStep;
import org.eclipse.tm.te.runtime.stepper.interfaces.IContextStepGroup;
import org.eclipse.tm.te.runtime.stepper.interfaces.IContextStepGroupable;
import org.eclipse.tm.te.runtime.stepper.interfaces.IExtendedContextStep;
import org.eclipse.tm.te.runtime.stepper.interfaces.tracing.ITraceIds;
import org.eclipse.tm.te.runtime.stepper.nls.Messages;
/**
* A default step group implementation.
*/
public class ContextStepGroup extends AbstractContextStepGroup {
private boolean locked;
private String baseOn;
private final List<ReferenceSubElement> references = new ArrayList<ReferenceSubElement>();
/**
* Step group reference sub element.
*/
protected final static class ReferenceSubElement implements org.eclipse.core.runtime.IExecutableExtension {
private String id;
private String secondaryId;
private String insertBefore;
private String insertAfter;
private String overwrite;
private boolean removable;
private boolean hidden;
private boolean disable;
private boolean singleton;
private final List<String> dependencies = new ArrayList<String>();
/**
* Returns the id of the referenced step or step group.
*
* @return The id of the referenced step or step group.
*/
public String getId() {
return id;
}
/**
* Returns the secondary id of the referenced step or step group.
*
* @return The secondary id or <code>null</code>.
*/
public String getSecondaryId() {
return secondaryId;
}
/**
* Sets the secondary id of the referenced step or step group.
*
* @return The secondary id or <code>null</code>.
*/
public void setSecondardId(String secondaryId) {
this.secondaryId = secondaryId;
}
/**
* Returns the id of the step or step group the referenced
* step or group shall be inserted before.
*
* @return The id or <code>null</code>.
*/
public String getInsertBefore() {
return insertBefore;
}
/**
* Returns the id of the step or step group the referenced
* step or group shall be inserted after.
*
* @return The id or <code>null</code>.
*/
public String getInsertAfter() {
return insertAfter;
}
/**
* Returns the id of the step or step group the referenced
* step or group do overwrite.
*
* @return The id or <code>null</code>.
*/
public String getOverwrite() {
return overwrite;
}
/**
* Returns if or if not the referenced step or step group
* can be removed by the user from the group.
*
* @return <code>True</code> if removable, <code>false</code> otherwise.
*/
public boolean isRemovable() {
return removable;
}
/**
* Returns if or if not the referenced step or step group
* is hidden.
*
* @return <code>True</code> if hidden, <code>false</code> otherwise.
*/
public boolean isHidden() {
return hidden;
}
/**
* Returns if or if not to disable the referenced step or step group.
*
* @return <code>True</code> if to disable, <code>false</code> otherwise.
*/
public boolean isDisable() {
return disable;
}
/**
* Returns if or if not the referenced step or step group is a singleton.
*
* @return <code>True</code> if singleton, <code>false</code> otherwise.
*/
public boolean isSingleton() {
return singleton;
}
/**
* Returns the list of dependencies.
* <p>
* The step or step group id might be fully qualified using the form
* <code>"primaryId##secondaryId</code>. The <code>secondaryId</code> is optional.
*
* @return The list of dependencies or an empty list.
*/
public String[] getDependencies() {
return dependencies.toArray(new String[dependencies.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
*/
@Override
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
if (config == null) {
return;
}
String value = config.getAttribute("id"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.id = value.trim();
}
value = config.getAttribute("secondaryId"); //$NON-NLS-1$
setSecondardId(value != null && value.trim().length() > 0 ? value.trim() : null);
value = config.getAttribute("insertBefore"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.insertBefore = value.trim();
}
value = config.getAttribute("insertAfter"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.insertAfter = value.trim();
}
value = config.getAttribute("overwrite"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.overwrite = value.trim();
}
value = config.getAttribute("removable"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.removable = Boolean.parseBoolean(value.trim());
}
value = config.getAttribute("hidden"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.hidden = Boolean.parseBoolean(value.trim());
}
value = config.getAttribute("disable"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.disable = Boolean.parseBoolean(value.trim());
}
value = config.getAttribute("singleton"); //$NON-NLS-1$
if (value != null && value.trim().length() > 0) {
this.singleton = Boolean.parseBoolean(value.trim());
}
// Read in the list of dependencies if specified.
dependencies.clear();
IConfigurationElement[] requires = config.getChildren("requires"); //$NON-NLS-1$
for (IConfigurationElement require : requires) {
value = require.getAttribute("id"); //$NON-NLS-1$
if (value == null || value.trim().length() == 0) {
throw new CoreException(new Status(IStatus.ERROR,
CoreBundleActivator.getUniqueIdentifier(),
0,
NLS.bind(Messages.AbstractContextStep_error_missingRequiredAttribute,
"dependency id (requires)", //$NON-NLS-1$
config.getName()),
null));
}
if (!dependencies.contains(value.trim())) {
dependencies.add(value.trim());
}
}
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (getId() != null && obj instanceof ReferenceSubElement) {
boolean secondaryIdEquals = false;
if (getSecondaryId() == null) {
secondaryIdEquals = ((ReferenceSubElement)obj).getSecondaryId() == null;
}
else {
secondaryIdEquals = getSecondaryId().equals(((ReferenceSubElement)obj).getSecondaryId());
}
return getId().equals(((ReferenceSubElement)obj).getId()) && secondaryIdEquals;
}
return super.equals(obj);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return getId() != null ? getId().hashCode() + (getSecondaryId() != null ? getSecondaryId().hashCode() : 0) : super.hashCode();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuffer buffer = new StringBuffer(getClass().getSimpleName());
buffer.append(": "); //$NON-NLS-1$
buffer.append("id = " + getId()); //$NON-NLS-1$
buffer.append(", secondaryId = " + getSecondaryId()); //$NON-NLS-1$
buffer.append(", insertBefore = " + getInsertBefore()); //$NON-NLS-1$
buffer.append(", insertAfter = " + getInsertAfter()); //$NON-NLS-1$
buffer.append(", overwrite = " + getOverwrite()); //$NON-NLS-1$
buffer.append(", removable = " + isRemovable()); //$NON-NLS-1$
buffer.append(", hidden = " + isHidden()); //$NON-NLS-1$
buffer.append(", disable = " + isDisable()); //$NON-NLS-1$
buffer.append(", singleton = " + isSingleton()); //$NON-NLS-1$
return buffer.toString();
}
}
/**
* Constructor.
*/
public ContextStepGroup() {
super();
locked = false;
baseOn = null;
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.stepper.AbstractContextStepGroup#isLocked()
*/
@Override
public boolean isLocked() {
return locked;
}
/**
* Returns the id of the step group this step group is
* initially based on.
*
* @return The id or <code>null</code>.
*/
protected String getBaseOn() {
return baseOn;
}
/**
* Returns the references.
*/
protected List<ReferenceSubElement> getReferences() {
return references;
}
/**
* Check for duplicates of the referenced step or step group. The check
* will fail if multiple occurrence of a step or step group are found and
* the step or step group is supposed to be a singleton.
*
* @param steps The list of steps. Must not be <code>null</code>.
* @param reference The reference. Must not be <code>null</code>.
* @param type The type id. Must not be <code>null</code>.
* @param mode The sub type id. Must not be <code>null</code>.
*
* @throws CoreException If multiple occurrences of singleton references are found.
*/
protected void checkForDuplicates(List<IContextStepGroupable> steps, ReferenceSubElement reference, String type, String mode) throws CoreException {
assert steps != null && reference != null;
// If the reference overwrites another reference, it is not a duplicate
String overwrite = reference.getOverwrite();
if (overwrite != null && overwrite.length() > 0) {
return;
}
boolean checkFailed = false;
for (IContextStepGroupable step : steps) {
if (step.getExtension().getId().equals(reference.getId())) {
// We've found an existing groupable with the reference id.
// If either the groupable, the reference or the extension is
// marked singleton, than this is an failure.
checkFailed = step.isSingleton() || reference.isSingleton()
|| (step.getExtension() instanceof IExtendedContextStep
&& ((IExtendedContextStep)step.getExtension()).isSingleton());
if (checkFailed) {
break;
}
}
}
if (checkFailed) {
throw new CoreException(new Status(IStatus.ERROR,
CoreBundleActivator.getUniqueIdentifier(),
MessageFormat.format(Messages.ContextStepGroup_error_multipleSingletonOccurrences,
NLS.bind(Messages.ContextStepGroup_error_stepGroup, getLabel()),
NLS.bind(Messages.ContextStepGroup_error_referencedStepOrGroup, reference.getId()),
NLS.bind(Messages.ContextStepGroup_error_typeAndMode, type, mode))
));
}
}
/**
* Replace all references to a given step or step group with another one.
*
* @param steps The list of steps. Must not be <code>null</code>.
* @param oldId The id of the step or step group to replace. Must not be <code>null</code>.
* @param replacement The replacement. Must not be <code>null</code>.
* @param reference The reference sub element. Must not be <code>null</code>.
*
* @return The list of affected groupable's or an empty list.
*/
protected List<IContextStepGroupable> onOverwrite(List<IContextStepGroupable> steps, String oldId, IExecutableExtension replacement, ReferenceSubElement reference) {
assert steps != null && oldId != null && replacement != null && reference != null;
List<IContextStepGroupable> affected = new ArrayList<IContextStepGroupable>();
// Isolate primary and secondary id
String primaryId = oldId;
String secondaryId = null;
String[] splitted = oldId.split("##", 2); //$NON-NLS-1$
if (splitted.length == 2) {
primaryId = splitted[0];
secondaryId = splitted[1];
}
for (IContextStepGroupable step : steps) {
// A step is clearly affected if the primary id and the secondary
// id (if any) matches the overwritten id
if (step.getExtension().getId().equals(primaryId)
&& (secondaryId == null || secondaryId.equals(step.getSecondaryId()))) {
if (step instanceof ContextStepGroupable) {
ContextStepGroupable groupable = ((ContextStepGroupable)step);
// Update the grouped extension
groupable.setExtension(replacement);
// Update the groupable secondary id
groupable.setSecondaryId(reference.getSecondaryId());
// Add the groupable to the list of affected steps
affected.add(step);
}
}
// A step is affected as well if the step depends on the overwritten step
// In this case we have to update the dependencies.
List<String> dependencies = new ArrayList<String>(Arrays.asList(step.getDependencies()));
if (dependencies.contains(oldId)) {
String fullId = replacement.getId() + (reference.getSecondaryId() != null ? "##" + reference.getSecondaryId() : ""); //$NON-NLS-1$ //$NON-NLS-2$
// We have to replace the dependency at the exact position within the list
dependencies.set(dependencies.indexOf(oldId), fullId);
if (step instanceof ContextStepGroupable) {
((ContextStepGroupable)step).setDependencies(dependencies.toArray(new String[dependencies.size()]));
}
}
}
return affected;
}
/**
* Insert the step before the specified step or step group. If no step or
* step group with the given id exist, the step is added to the end.
*
* @param steps The list of steps. Must not be <code>null</code>.
* @param id The id of the step or step group where to insert the new step before. Must not be <code>null</code>.
* @param newStep The step to add. Must not be <code>null</code>.
* @param reference The reference sub element. Must not be <code>null</code>.
*
* @return The list of affected groupable's or an empty list.
*/
protected List<IContextStepGroupable> onInsertBefore(List<IContextStepGroupable> steps, String id, IExecutableExtension newStep, ReferenceSubElement reference) {
assert steps != null && id != null && newStep != null && reference != null;
List<IContextStepGroupable> affected = new ArrayList<IContextStepGroupable>();
// Isolate primary and secondary id
String primaryId = id;
String secondaryId = null;
String[] splitted = id.split("##", 2); //$NON-NLS-1$
if (splitted.length == 2) {
primaryId = splitted[0];
secondaryId = splitted[1];
}
// Always loop over all steps in case the anchor step is available
// multiple times. In such case, the new step is inserted at all
// occurrences.
for (int i = 0; i < steps.size(); i++) {
IContextStepGroupable step = steps.get(i);
if (!step.getExtension().getId().equals(primaryId)) {
continue;
}
if (secondaryId != null && !secondaryId.equals(step.getSecondaryId())) {
continue;
}
// Create a new groupable object for inserting
IContextStepGroupable groupable = new ContextStepGroupable(newStep, reference.getSecondaryId());
// Insert the new step at the current position
steps.add(i, groupable);
// And increase the counter --> Otherwise we would see the
// same step we want to insert before again!
i++;
// Add the new groupable to the list of affected steps
affected.add(groupable);
}
// If the step could not be added, add to the end of the list
if (affected.isEmpty()) {
IContextStepGroupable groupable = new ContextStepGroupable(newStep, reference.getSecondaryId());
steps.add(groupable);
}
return affected;
}
/**
* Insert the step after the specified step or step group. If no step or
* step group with the given id exist, the step is added to the end.
*
* @param steps The list of steps. Must not be <code>null</code>.
* @param id The id of the step or step group where to insert the new step after. Must not be <code>null</code>.
* @param newStep The step to add. Must not be <code>null</code>.
* @param reference The reference sub element. Must not be <code>null</code>.
*
* @return The list of affected groupable's or an empty list.
*/
protected List<IContextStepGroupable> onInsertAfter(List<IContextStepGroupable> steps, String id, IExecutableExtension newStep, ReferenceSubElement reference) {
assert steps != null && id != null && newStep != null && reference != null;
List<IContextStepGroupable> affected = new ArrayList<IContextStepGroupable>();
// Isolate primary and secondary id
String primaryId = id;
String secondaryId = null;
String[] splitted = id.split("##", 2); //$NON-NLS-1$
if (splitted.length == 2) {
primaryId = splitted[0];
secondaryId = splitted[1];
}
// Always loop over all steps in case the anchor step is available
// multiple times. In such case, the new step is inserted at all
// occurrences.
for (int i = 0; i < steps.size(); i++) {
IContextStepGroupable step = steps.get(i);
if (!step.getExtension().getId().equals(primaryId)) {
continue;
}
if (secondaryId != null && !secondaryId.equals(step.getSecondaryId())) {
continue;
}
// Create a new groupable object for inserting
IContextStepGroupable groupable = new ContextStepGroupable(newStep, reference.getSecondaryId());
// Insert the new groupable after the current step or at the end (if i + 1 == steps.size())
steps.add(i + 1, groupable);
// Add the new groupable to the list of affected steps
affected.add(groupable);
}
// If the step could not be added, add to the end of the list
if (affected.isEmpty()) {
IContextStepGroupable groupable = new ContextStepGroupable(newStep, reference.getSecondaryId());
steps.add(groupable);
}
return affected;
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.stepper.interfaces.IContextStepGroup#getSteps(java.lang.String, java.lang.String)
*/
@Override
public IContextStepGroupable[] getSteps(String type, String mode) throws CoreException {
assert type != null && mode != null;
// The list of resolved steps for the specified type and mode
List<IContextStepGroupable> steps = new ArrayList<IContextStepGroupable>();
// If this step group is based on another step group, we have to get the resolved
// steps from there first.
if (getBaseOn() != null) {
IContextStepGroup baseStepGroup = getStepGroup(getBaseOn());
// If the base step group cannot be found, that's an error. We cannot continue
// without the base group.
if (baseStepGroup == null) {
throw new CoreException(new Status(IStatus.ERROR,
CoreBundleActivator.getUniqueIdentifier(),
MessageFormat.format(Messages.ContextStepGroup_error_missingBaseStepGroup,
NLS.bind(Messages.ContextStepGroup_error_stepGroup, getLabel()),
NLS.bind(Messages.ContextStepGroup_error_referencedBaseGroup, getBaseOn()),
NLS.bind(Messages.ContextStepGroup_error_typeAndMode, type, mode))
));
}
// Add all the steps from the base step group now to the list
steps.addAll(Arrays.asList(baseStepGroup.getSteps(type, mode)));
}
// Now process the references and modify the steps list accordingly
for (ReferenceSubElement reference : getReferences()) {
// Get the step or step group for the referenced id. Try the steps first.
IExecutableExtension candidate = getStep(reference.getId());
if (candidate == null) {
candidate = getStepGroup(reference.getId());
}
// If the candidate is null here, that's an error as a referenced step is missing.
if (candidate == null) {
throw new CoreException(new Status(IStatus.ERROR,
CoreBundleActivator.getUniqueIdentifier(),
MessageFormat.format(Messages.ContextStepGroup_error_missingReferencedStep,
NLS.bind(Messages.ContextStepGroup_error_stepGroup, getLabel()),
NLS.bind(Messages.ContextStepGroup_error_referencedStepOrGroup, reference.getId()),
NLS.bind(Messages.ContextStepGroup_error_typeAndMode, type, mode))
));
}
// Check if the step is valid for the current launch configuration type and mode.
if (candidate instanceof IContextStep) {
boolean valid = isValidStep(candidate.getId(), type, mode);
if (!valid) {
CoreBundleActivator.getTraceHandler().trace(
"StepGroup#getSteps: SKIPPED step = '" + candidate.getLabel() + "'." //$NON-NLS-1$ //$NON-NLS-2$
+ " Not valid for type id '" + type + "'" //$NON-NLS-1$ //$NON-NLS-2$
+ " and mode '" + mode + "'", //$NON-NLS-1$ //$NON-NLS-2$
0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this);
continue;
}
}
// Check for duplicates of singleton references.
checkForDuplicates(steps, reference, type, mode);
// Check for the steps own dependencies to be valid for the current type id and mode
if (candidate instanceof IContextStep) {
checkForDependenciesValid((IContextStep) candidate, type, mode);
}
// Will contain the list of affected groupables from the whole list
List<IContextStepGroupable> affectedGroupables = new ArrayList<IContextStepGroupable>();
// Check first for overwriting groupables
String overwrite = reference.getOverwrite();
if (overwrite != null && overwrite.length() > 0) {
affectedGroupables.addAll(onOverwrite(steps, overwrite, candidate, reference));
} else {
// overwrite is not set -> process insertBefore or insertAfter
String insertBefore = reference.getInsertBefore();
String insertAfter = reference.getInsertAfter();
// If neither one is specified, the step or step group will be to the end of the list
if ((insertBefore == null || insertBefore.length() == 0)
&& (insertAfter == null || insertAfter.length() == 0)) {
IContextStepGroupable groupable = new ContextStepGroupable(candidate, reference.getSecondaryId());
steps.add(groupable);
affectedGroupables.add(groupable);
} else {
// insertBefore comes first
if (insertBefore != null && insertBefore.length() > 0) {
affectedGroupables.addAll(onInsertBefore(steps, insertBefore, candidate, reference));
} else {
affectedGroupables.addAll(onInsertAfter(steps, insertAfter, candidate, reference));
}
}
}
// Process the groupable attributes on all affected groupables
for (IContextStepGroupable step : affectedGroupables) {
if (!(step instanceof ContextStepGroupable)) {
continue;
}
ContextStepGroupable groupable = (ContextStepGroupable)step;
groupable.setDependencies(reference.getDependencies());
if (!reference.isRemovable() && groupable.isRemovable()) {
groupable.setRemovable(reference.isRemovable());
}
if (reference.isHidden() && !groupable.isHidden()) {
groupable.setHidden(reference.isHidden());
}
if (reference.isDisable() && !groupable.isDisabled()) {
groupable.setDisabled(reference.isDisable());
}
if (reference.isSingleton() && !groupable.isSingleton()) {
groupable.setSingleton(reference.isSingleton());
}
}
}
return !steps.isEmpty() ? steps.toArray(new IContextStepGroupable[steps.size()]) : NO_STEPS;
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.extensions.ExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
*/
@Override
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
references.clear();
super.setInitializationData(config, propertyName, data);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.stepper.extensions.AbstractContextStepGroup#doSetInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
*/
@Override
public void doSetInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
super.doSetInitializationData(config, propertyName, data);
if (!locked) {
String lockedAttribute = config.getAttribute("locked"); //$NON-NLS-1$
if (lockedAttribute != null) {
this.locked = Boolean.parseBoolean(lockedAttribute);
}
}
if (baseOn == null || baseOn.trim().length() == 0) {
String baseOnAttribute = config.getAttribute("baseOn"); //$NON-NLS-1$
if (baseOnAttribute != null && baseOnAttribute.trim().length() > 0) {
this.baseOn = baseOnAttribute.trim();
}
}
Map<String, Integer> occurrences = new HashMap<String, Integer>();
IConfigurationElement[] childElements = config.getChildren("references"); //$NON-NLS-1$
for (IConfigurationElement childElement : childElements) {
IConfigurationElement[] references = childElement.getChildren("reference"); //$NON-NLS-1$
for (IConfigurationElement reference : references) {
ReferenceSubElement candidate = new ReferenceSubElement();
candidate.setInitializationData(reference, reference.getName(), null);
// If multiple references to the same step or step group exist, check
// for the secondaryId
if (occurrences.containsKey(candidate.getId())) {
// Occurrences are counted up always
int number = occurrences.get(candidate.getId()).intValue() + 1;
occurrences.put(candidate.getId(), Integer.valueOf(number));
if (candidate.getSecondaryId() == null) {
// secondaryId not explicitly set -> auto set
candidate.setSecondardId(Integer.toString(number));
}
} else {
// remember the occurrence of the reference
occurrences.put(candidate.getId(), Integer.valueOf(1));
}
// References are not sorted out here. That's the task of the resolver.
this.references.add(candidate);
}
}
}
/**
* Checks is all dependencies of the given step are available
* and valid for the given type id and mode.
*
* @param step The step. Must not be <code>null</code>.
* @param type The type id. Must not be <code>null</code>.
* @param mode The mode. Must not be <code>null</code>.
*
* @throws CoreException If a required step or step group is not available or not valid.
*/
protected void checkForDependenciesValid(IContextStep step, String type, String mode) throws CoreException {
assert step != null && type != null && mode != null;
String[] dependencies = step.getDependencies();
for (String dependency : dependencies) {
// Get the step or step group. Try the steps first.
IExecutableExtension candidate = getStep(dependency);
if (candidate == null) {
candidate = getStepGroup(dependency);
}
// If the candidate is null here, that's an error as a required step or step group is missing.
if (candidate == null) {
throw new CoreException(new Status(IStatus.ERROR,
CoreBundleActivator.getUniqueIdentifier(),
MessageFormat.format(Messages.ContextStepGroup_error_missingRequiredStep,
NLS.bind(Messages.ContextStepGroup_error_step, step.getLabel()),
NLS.bind(Messages.ContextStepGroup_error_requiredStepOrGroup, dependency),
NLS.bind(Messages.ContextStepGroup_error_typeAndMode, type, mode))
));
}
// If the candidate a step, validate the step
if (candidate instanceof IContextStep) {
IContextStep candidateStep = (IContextStep)candidate;
boolean valid = isValidStep(candidateStep.getId(), type, mode);
if (!valid) {
throw new CoreException(new Status(IStatus.ERROR,
CoreBundleActivator.getUniqueIdentifier(),
MessageFormat.format(Messages.ContextStepGroup_error_invalidRequiredStep,
NLS.bind(Messages.ContextStepGroup_error_step, step.getLabel()),
NLS.bind(Messages.ContextStepGroup_error_requiredStep, dependency),
NLS.bind(Messages.ContextStepGroup_error_typeAndMode, type, mode))
));
}
// Step is valid -> recursively check required steps.
checkForDependenciesValid(candidateStep, type, mode);
}
}
}
/**
* Convenience method returning a unique instance of the step
* identified by the given id.
*
* @param id The step id. Must not be <code>null</code>.
* @return The step instance or <code>null</code>.
*/
protected IContextStep getStep(String id) {
Assert.isNotNull(id);
return StepperManager.getInstance().getStepExtManager().getStep(id, true);
}
/**
* Convenience method returning a unique instance of the step
* group identified by the given id.
*
* @param id The step id. Must not be <code>null</code>.
* @return The step group instance or <code>null</code>.
*/
protected IContextStepGroup getStepGroup(String id) {
Assert.isNotNull(id);
return StepperManager.getInstance().getStepGroupExtManager().getStepGroup(id, true);
}
/**
* Returns if or if not the step identified by the given id is valid.
* <p>
* <b>Note:</b> The default implementation returns always <code>true</code>.
*
* @param id The step id. Must not be <code>null</code>.
* @param type The type id. Must not be <code>null</code>.
* @param mode The mode. Must not be <code>null</code>.
*
* @return <code>True</code> if the step is valid, <code>false</code> otherwise.
*/
protected boolean isValidStep(String id, String type, String mode) {
return true;
}
}