diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tm.te.runtime.stepper/src/org/eclipse/tm/te/runtime/stepper/extensions/ContextStepGroup.java')
-rw-r--r-- | target_explorer/plugins/org.eclipse.tm.te.runtime.stepper/src/org/eclipse/tm/te/runtime/stepper/extensions/ContextStepGroup.java | 814 |
1 files changed, 814 insertions, 0 deletions
diff --git a/target_explorer/plugins/org.eclipse.tm.te.runtime.stepper/src/org/eclipse/tm/te/runtime/stepper/extensions/ContextStepGroup.java b/target_explorer/plugins/org.eclipse.tm.te.runtime.stepper/src/org/eclipse/tm/te/runtime/stepper/extensions/ContextStepGroup.java new file mode 100644 index 000000000..626301c7d --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.runtime.stepper/src/org/eclipse/tm/te/runtime/stepper/extensions/ContextStepGroup.java @@ -0,0 +1,814 @@ +/******************************************************************************* + * 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; + } +} |