Java tutorial
package org.apache.taverna.scufl2.api.common; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.xml.bind.PropertyException; import org.apache.taverna.scufl2.api.activity.Activity; import org.apache.taverna.scufl2.api.annotation.Annotation; import org.apache.taverna.scufl2.api.common.Visitor.VisitorWithPath; import org.apache.taverna.scufl2.api.configurations.Configuration; import org.apache.taverna.scufl2.api.container.WorkflowBundle; import org.apache.taverna.scufl2.api.core.BlockingControlLink; import org.apache.taverna.scufl2.api.core.ControlLink; import org.apache.taverna.scufl2.api.core.DataLink; import org.apache.taverna.scufl2.api.core.Processor; import org.apache.taverna.scufl2.api.core.Workflow; import org.apache.taverna.scufl2.api.iterationstrategy.CrossProduct; import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyStack; import org.apache.taverna.scufl2.api.iterationstrategy.PortNode; import org.apache.taverna.scufl2.api.port.ActivityPort; import org.apache.taverna.scufl2.api.port.InputActivityPort; import org.apache.taverna.scufl2.api.port.InputPort; import org.apache.taverna.scufl2.api.port.InputProcessorPort; import org.apache.taverna.scufl2.api.port.OutputActivityPort; import org.apache.taverna.scufl2.api.port.OutputPort; import org.apache.taverna.scufl2.api.port.OutputProcessorPort; import org.apache.taverna.scufl2.api.port.Port; import org.apache.taverna.scufl2.api.port.ProcessorPort; import org.apache.taverna.scufl2.api.port.ReceiverPort; import org.apache.taverna.scufl2.api.port.SenderPort; import org.apache.taverna.scufl2.api.profiles.ProcessorBinding; import org.apache.taverna.scufl2.api.profiles.ProcessorInputPortBinding; import org.apache.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; import org.apache.taverna.scufl2.api.profiles.ProcessorPortBinding; import org.apache.taverna.scufl2.api.profiles.Profile; import com.fasterxml.jackson.databind.JsonNode; /** * Utility methods for dealing with SCUFL2 models * * @author Stian Soiland-Reyes */ public class Scufl2Tools { private static final String CONSTANT_STRING = "string"; private static final String CONSTANT_VALUE_PORT = "value"; public static URI PORT_DEFINITION = URI.create("http://ns.taverna.org.uk/2010/scufl2#portDefinition"); private static URITools uriTools = new URITools(); public static URI NESTED_WORKFLOW = URI.create("http://ns.taverna.org.uk/2010/activity/nested-workflow"); /** * Compare {@link ProcessorBinding}s by their * {@link ProcessorBinding#getActivityPosition()}. * <p> * <b>Note:</b> this comparator imposes orderings that are inconsistent with * equals. * * @author Stian Soiland-Reyes */ public static class BindingComparator implements Comparator<ProcessorBinding> { @Override public int compare(ProcessorBinding o1, ProcessorBinding o2) { return o1.getActivityPosition() - o2.getActivityPosition(); } } public List<Annotation> annotationsFor(Child<?> bean) { WorkflowBundle bundle = findParent(WorkflowBundle.class, bean); return annotationsFor(bean, bundle); } public List<Annotation> annotationsFor(WorkflowBean bean, WorkflowBundle bundle) { ArrayList<Annotation> annotations = new ArrayList<>(); if (bundle == null) return annotations; for (Annotation ann : bundle.getAnnotations()) if (ann.getTarget().equals(bean)) annotations.add(ann); return annotations; } /** * Returns the {@link Configuration} for a {@link Configurable} in the given * {@link Profile}. * * @param configurable * the <code>Configurable</code> to find a * <code>Configuration</code> for * @param profile * the <code>Profile</code> to look for the * <code>Configuration</code> in * @return the <code>Configuration</code> for a <code>Configurable</code> in * the given <code>Profile</code> */ public Configuration configurationFor(Configurable configurable, Profile profile) { List<Configuration> configurations = configurationsFor(configurable, profile); if (configurations.isEmpty()) throw new IndexOutOfBoundsException("Could not find configuration for " + configurable); if (configurations.size() > 1) throw new IllegalStateException("More than one configuration for " + configurable); return configurations.get(0); } public Configuration configurationForActivityBoundToProcessor(Processor processor, Profile profile) { ProcessorBinding binding = processorBindingForProcessor(processor, profile); return configurationFor(binding.getBoundActivity(), profile); } /** * Returns the list of {@link Configuration Configurations} for a * {@link Configurable} in the given {@link Profile}. * * @param configurable * the <code>Configurable</code> to find a * <code>Configuration</code> for * @param profile * the <code>Profile</code> to look for the * <code>Configuration</code> in * @return the list of <code>Configurations</code> for a * <code>Configurable</code> in the given <code>Profile</code> */ public List<Configuration> configurationsFor(Configurable configurable, Profile profile) { List<Configuration> configurations = new ArrayList<>(); for (Configuration config : profile.getConfigurations()) if (configurable.equals(config.getConfigures())) configurations.add(config); // Collections.sort(configurations); return configurations; } @SuppressWarnings("unchecked") public List<BlockingControlLink> controlLinksBlocking(Processor blocked) { List<BlockingControlLink> controlLinks = new ArrayList<>(); for (ControlLink link : blocked.getParent().getControlLinks()) { if (!(link instanceof BlockingControlLink)) continue; BlockingControlLink blockingControlLink = (BlockingControlLink) link; if (blockingControlLink.getBlock().equals(blocked)) controlLinks.add(blockingControlLink); } Collections.sort(controlLinks); return controlLinks; } @SuppressWarnings("unchecked") public List<BlockingControlLink> controlLinksWaitingFor(Processor untilFinished) { List<BlockingControlLink> controlLinks = new ArrayList<>(); for (ControlLink link : untilFinished.getParent().getControlLinks()) { if (!(link instanceof BlockingControlLink)) continue; BlockingControlLink blockingControlLink = (BlockingControlLink) link; if (blockingControlLink.getUntilFinished().equals(untilFinished)) controlLinks.add(blockingControlLink); } Collections.sort(controlLinks); return controlLinks; } @SuppressWarnings("unchecked") public List<DataLink> datalinksFrom(SenderPort senderPort) { Workflow wf = findParent(Workflow.class, (Child<Workflow>) senderPort); List<DataLink> links = new ArrayList<>(); for (DataLink link : wf.getDataLinks()) if (link.getReceivesFrom().equals(senderPort)) links.add(link); Collections.sort(links); return links; } @SuppressWarnings("unchecked") public List<DataLink> datalinksTo(ReceiverPort receiverPort) { Workflow wf = findParent(Workflow.class, (Child<Workflow>) receiverPort); List<DataLink> links = new ArrayList<>(); for (DataLink link : wf.getDataLinks()) if (link.getSendsTo().equals(receiverPort)) links.add(link); Collections.sort(links); return links; } public <T extends WorkflowBean> T findParent(Class<T> parentClass, Child<?> child) { WorkflowBean parent = child.getParent(); if (parent == null) return null; if (parentClass.isAssignableFrom(parent.getClass())) { @SuppressWarnings("unchecked") T foundParent = (T) parent; return foundParent; } if (parent instanceof Child) return findParent(parentClass, (Child<?>) parent); return null; } public JsonNode portDefinitionFor(ActivityPort activityPort, Profile profile) throws PropertyException { Configuration actConfig = configurationFor(activityPort.getParent(), profile); JsonNode portDef = actConfig.getJson().get("portDefinition"); if (portDef == null) return null; URI portPath = uriTools.relativeUriForBean(activityPort, activityPort.getParent()); // e.g. "in/input1" or "out/output2" return portDef.get(portPath.toString()); } public ProcessorBinding processorBindingForProcessor(Processor processor, Profile profile) { List<ProcessorBinding> bindings = processorBindingsForProcessor(processor, profile); if (bindings.isEmpty()) throw new IndexOutOfBoundsException("Could not find bindings for " + processor); if (bindings.size() > 1) throw new IllegalStateException("More than one proc binding for " + processor); return bindings.get(0); } public List<ProcessorBinding> processorBindingsForProcessor(Processor processor, Profile profile) { List<ProcessorBinding> bindings = new ArrayList<>(); for (ProcessorBinding pb : profile.getProcessorBindings()) if (pb.getBoundProcessor().equals(processor)) bindings.add(pb); Collections.sort(bindings, new BindingComparator()); return bindings; } public List<ProcessorBinding> processorBindingsToActivity(Activity activity) { Profile profile = activity.getParent(); List<ProcessorBinding> bindings = new ArrayList<>(); for (ProcessorBinding pb : profile.getProcessorBindings()) if (pb.getBoundActivity().equals(activity)) bindings.add(pb); Collections.sort(bindings, new BindingComparator()); return bindings; } public ProcessorInputPortBinding processorPortBindingForPort(InputPort inputPort, Profile profile) { return (ProcessorInputPortBinding) processorPortBindingForPortInternal(inputPort, profile); } public ProcessorOutputPortBinding processorPortBindingForPort(OutputPort outputPort, Profile profile) { return (ProcessorOutputPortBinding) processorPortBindingForPortInternal(outputPort, profile); } protected ProcessorPortBinding<?, ?> processorPortBindingForPortInternal(Port port, Profile profile) { List<ProcessorBinding> processorBindings; if (port instanceof ProcessorPort) { ProcessorPort processorPort = (ProcessorPort) port; processorBindings = processorBindingsForProcessor(processorPort.getParent(), profile); } else if (port instanceof ActivityPort) { ActivityPort activityPort = (ActivityPort) port; processorBindings = processorBindingsToActivity(activityPort.getParent()); } else throw new IllegalArgumentException("Port must be a ProcessorPort or ActivityPort"); for (ProcessorBinding procBinding : processorBindings) { ProcessorPortBinding<?, ?> portBinding = processorPortBindingInternalInBinding(port, procBinding); if (portBinding != null) return portBinding; } return null; } protected ProcessorPortBinding<?, ?> processorPortBindingInternalInBinding(Port port, ProcessorBinding procBinding) { Set<? extends ProcessorPortBinding<?, ?>> portBindings; if (port instanceof InputPort) portBindings = procBinding.getInputPortBindings(); else portBindings = procBinding.getOutputPortBindings(); for (ProcessorPortBinding<?, ?> portBinding : portBindings) { if (port instanceof ProcessorPort && portBinding.getBoundProcessorPort().equals(port)) return portBinding; if (port instanceof ActivityPort && portBinding.getBoundActivityPort().equals(port)) return portBinding; } return null; } public void setParents(WorkflowBundle bundle) { bundle.accept(new VisitorWithPath() { @Override public boolean visit() { WorkflowBean node = getCurrentNode(); if (node instanceof Child) { @SuppressWarnings("unchecked") Child<WorkflowBean> child = (Child<WorkflowBean>) node; WorkflowBean parent = getCurrentPath().peek(); if (child.getParent() != parent) child.setParent(parent); } return true; } }); } /** * Find processors that a given processor can connect to downstream. * <p> * This is calculated as all processors in the dataflow, except the * processor itself, and any processor <em>upstream</em>, following both * data links and conditional links. * * @see #possibleUpStreamProcessors(Dataflow, Processor) * @see #splitProcessors(Collection, Processor) * * @param dataflow * Dataflow from where to find processors * @param processor * Processor which is to be connected * @return A set of possible downstream processors */ public Set<Processor> possibleDownStreamProcessors(Workflow dataflow, Processor processor) { ProcessorSplit splitProcessors = splitProcessors(dataflow.getProcessors(), processor); Set<Processor> possibles = new HashSet<>(splitProcessors.getUnconnected()); possibles.addAll(splitProcessors.getDownStream()); return possibles; } /** * Find processors that a given processor can connect to upstream. * <p> * This is calculated as all processors in the dataflow, except the * processor itself, and any processor <em>downstream</em>, following both * data links and conditional links. * * @see #possibleDownStreamProcessors(Dataflow, Processor) * @see #splitProcessors(Collection, Processor) * * @param dataflow * Dataflow from where to find processors * @param processor * Processor which is to be connected * @return A set of possible downstream processors */ public Set<Processor> possibleUpStreamProcessors(Workflow dataflow, Processor firstProcessor) { ProcessorSplit splitProcessors = splitProcessors(dataflow.getProcessors(), firstProcessor); Set<Processor> possibles = new HashSet<>(splitProcessors.getUnconnected()); possibles.addAll(splitProcessors.getUpStream()); return possibles; } /** * @param processors * @param splitPoint * @return */ public ProcessorSplit splitProcessors(Collection<Processor> processors, Processor splitPoint) { Set<Processor> upStream = new HashSet<>(); Set<Processor> downStream = new HashSet<>(); Set<Processor> queue = new HashSet<>(); queue.add(splitPoint); // First let's go upstream while (!queue.isEmpty()) { Processor processor = queue.iterator().next(); queue.remove(processor); List<BlockingControlLink> preConditions = controlLinksBlocking(processor); for (BlockingControlLink condition : preConditions) { Processor upstreamProc = condition.getUntilFinished(); if (!upStream.contains(upstreamProc)) { upStream.add(upstreamProc); queue.add(upstreamProc); } } for (InputProcessorPort inputPort : processor.getInputPorts()) for (DataLink incomingLink : datalinksTo(inputPort)) { SenderPort source = incomingLink.getReceivesFrom(); if (!(source instanceof OutputProcessorPort)) continue; Processor upstreamProc = ((OutputProcessorPort) source).getParent(); if (!upStream.contains(upstreamProc)) { upStream.add(upstreamProc); queue.add(upstreamProc); } } } // Our split queue.add(splitPoint); // Then downstream while (!queue.isEmpty()) { Processor processor = queue.iterator().next(); queue.remove(processor); List<BlockingControlLink> controlledConditions = controlLinksWaitingFor(processor); for (BlockingControlLink condition : controlledConditions) { Processor downstreamProc = condition.getBlock(); if (!downStream.contains(downstreamProc)) { downStream.add(downstreamProc); queue.add(downstreamProc); } } for (OutputProcessorPort outputPort : processor.getOutputPorts()) for (DataLink datalink : datalinksFrom(outputPort)) { ReceiverPort sink = datalink.getSendsTo(); if (!(sink instanceof InputProcessorPort)) continue; Processor downstreamProcc = ((InputProcessorPort) sink).getParent(); if (!downStream.contains(downstreamProcc)) { downStream.add(downstreamProcc); queue.add(downstreamProcc); } } } Set<Processor> undecided = new HashSet<>(processors); undecided.remove(splitPoint); undecided.removeAll(upStream); undecided.removeAll(downStream); return new ProcessorSplit(splitPoint, upStream, downStream, undecided); } /** * Result bean returned from * {@link Scufl2Tools#splitProcessors(Collection, Processor)}. * * @author Stian Soiland-Reyes */ public static class ProcessorSplit { private final Processor splitPoint; private final Set<Processor> upStream; private final Set<Processor> downStream; private final Set<Processor> unconnected; /** * Processor that was used as a split point. * * @return Split point processor */ public Processor getSplitPoint() { return splitPoint; } /** * Processors that are upstream from the split point. * * @return Upstream processors */ public Set<Processor> getUpStream() { return upStream; } /** * Processors that are downstream from the split point. * * @return Downstream processors */ public Set<Processor> getDownStream() { return downStream; } /** * Processors that are unconnected to the split point. * <p> * These are processors in the dataflow that are neither upstream, * downstream or the split point itself. * <p> * Note that this does not imply a total graph separation, for instance * processors in {@link #getUpStream()} might have some of these * unconnected processors downstream, but not along the path to the * {@link #getSplitPoint()}, or they could be upstream from any * processor in {@link #getDownStream()}. * * @return Processors unconnected from the split point */ public Set<Processor> getUnconnected() { return unconnected; } /** * Construct a new processor split result. * * @param splitPoint * Processor used as split point * @param upStream * Processors that are upstream from split point * @param downStream * Processors that are downstream from split point * @param unconnected * The rest of the processors, that are by definition * unconnected to split point */ public ProcessorSplit(Processor splitPoint, Set<Processor> upStream, Set<Processor> downStream, Set<Processor> unconnected) { this.splitPoint = splitPoint; this.upStream = upStream; this.downStream = downStream; this.unconnected = unconnected; } } /** * Return nested workflow for processor as configured in given profile. * <p> * A nested workflow is an activity bound to the processor with the * configurable type equal to {@value #NESTED_WORKFLOW}. * <p> * This method returns <code>null</code> if no such workflow was found, * otherwise the configured workflow. * <p * Note that even if several bindings/configurations map to a different * workflow, this method throws an IllegalStateException. Most workflows * will only have a single workflow for a given profile, to handle more * complex cases use instead * {@link #nestedWorkflowsForProcessor(Processor, Profile)}. * * @throws NullPointerException * if the given profile does not have a parent * @throws IllegalStateException * if a nested workflow configuration is invalid, or more than * one possible workflow is found * * @param processor * Processor which might have a nested workflow * @param profile * Profile to look for nested workflow activity/configuration. * The profile must have a {@link WorkflowBundle} set as its * {@link Profile#setParent(WorkflowBundle)}. * @return The configured nested workflows for processor */ public Workflow nestedWorkflowForProcessor(Processor processor, Profile profile) { List<Workflow> wfs = nestedWorkflowsForProcessor(processor, profile); if (wfs.isEmpty()) return null; if (wfs.size() > 1) throw new IllegalStateException("More than one possible workflow for processor " + processor); return wfs.get(0); } /** * Return list of nested workflows for processor as configured in given * profile. * <p> * A nested workflow is an activity bound to the processor with the * configurable type equal to {@value #NESTED_WORKFLOW}. * <p> * This method returns a list of 0 or more workflows, as every matching * {@link ProcessorBinding} and every matching {@link Configuration} for the * bound activity is considered. Normally there will only be a single nested * workflow, in which case the * {@link #nestedWorkflowForProcessor(Processor, Profile)} method should be * used instead. * <p> * Note that even if several bindings/configurations map to the same * workflow, each workflow is only included once in the list. Nested * workflow configurations that are incomplete or which #workflow can't be * found within the workflow bundle of the profile will be silently ignored. * * @throws NullPointerException * if the given profile does not have a parent * @throws IllegalStateException * if a nested workflow configuration is invalid * * @param processor * Processor which might have a nested workflow * @param profile * Profile to look for nested workflow activity/configuration. * The profile must have a {@link WorkflowBundle} set as its * {@link Profile#setParent(WorkflowBundle)}. * @return List of configured nested workflows for processor */ public List<Workflow> nestedWorkflowsForProcessor(Processor processor, Profile profile) { WorkflowBundle bundle = profile.getParent(); if (bundle == null) throw new NullPointerException("Parent must be set for " + profile); ArrayList<Workflow> workflows = new ArrayList<>(); for (ProcessorBinding binding : processorBindingsForProcessor(processor, profile)) { if (!binding.getBoundActivity().getType().equals(NESTED_WORKFLOW)) continue; for (Configuration c : configurationsFor(binding.getBoundActivity(), profile)) { JsonNode nested = c.getJson().get("nestedWorkflow"); Workflow wf = bundle.getWorkflows().getByName(nested.asText()); if (wf != null && !workflows.contains(wf)) workflows.add(wf); } } return workflows; } /** * Returns true if processor contains a nested workflow in any of its * activities in any of its profiles. */ public boolean containsNestedWorkflow(Processor processor) { for (Profile profile : processor.getParent().getParent().getProfiles()) if (containsNestedWorkflow(processor, profile)) return true; return false; } /** * Returns true if processor contains a nested workflow in the specified * profile. */ public boolean containsNestedWorkflow(Processor processor, Profile profile) { for (ProcessorBinding binding : processorBindingsForProcessor(processor, profile)) if (binding.getBoundActivity().getType().equals(NESTED_WORKFLOW)) return true; return false; } public void createActivityPortsFromProcessor(Activity activity, Processor processor) { for (InputProcessorPort processorPort : processor.getInputPorts()) new InputActivityPort(activity, processorPort.getName()).setDepth(processorPort.getDepth()); for (OutputProcessorPort processorPort : processor.getOutputPorts()) { OutputActivityPort activityPort = new OutputActivityPort(activity, processorPort.getName()); activityPort.setDepth(processorPort.getDepth()); activityPort.setGranularDepth(processorPort.getGranularDepth()); } } public void createProcessorPortsFromActivity(Processor processor, Activity activity) { for (InputActivityPort activityPort : activity.getInputPorts()) new InputProcessorPort(processor, activityPort.getName()).setDepth(activityPort.getDepth()); for (OutputActivityPort activityPort : activity.getOutputPorts()) { OutputProcessorPort procPort = new OutputProcessorPort(processor, activityPort.getName()); procPort.setDepth(activityPort.getDepth()); procPort.setGranularDepth(activityPort.getGranularDepth()); } } public ProcessorBinding bindActivityToProcessorByMatchingPorts(Activity activity, Processor processor) { ProcessorBinding binding = new ProcessorBinding(); binding.setParent(activity.getParent()); binding.setBoundActivity(activity); binding.setBoundProcessor(processor); bindActivityToProcessorByMatchingPorts(binding); return binding; } public void bindActivityToProcessorByMatchingPorts(ProcessorBinding binding) { Activity activity = binding.getBoundActivity(); Processor processor = binding.getBoundProcessor(); for (InputActivityPort activityPort : activity.getInputPorts()) { InputProcessorPort processorPort = processor.getInputPorts().getByName(activityPort.getName()); if (processorPort != null && processorPortBindingInternalInBinding(processorPort, binding) == null) new ProcessorInputPortBinding(binding, processorPort, activityPort); } for (OutputProcessorPort processorPort : processor.getOutputPorts()) { OutputActivityPort activityPort = activity.getOutputPorts().getByName(processorPort.getName()); if (activityPort != null && processorPortBindingInternalInBinding(activityPort, binding) == null) new ProcessorOutputPortBinding(binding, activityPort, processorPort); } } public ProcessorBinding createProcessorAndBindingFromActivity(Activity activity) { Processor proc = new Processor(); proc.setName(activity.getName()); createProcessorPortsFromActivity(proc, activity); return bindActivityToProcessorByMatchingPorts(activity, proc); } public Activity createActivityFromProcessor(Processor processor, Profile profile) { Activity activity = new Activity(); activity.setName(processor.getName()); activity.setParent(profile); createActivityPortsFromProcessor(activity, processor); bindActivityToProcessorByMatchingPorts(activity, processor); return activity; } public void removePortsBindingForUnknownPorts(ProcessorBinding binding) { // First, remove ports no longer owned by processor Iterator<ProcessorInputPortBinding> inputBindings = binding.getInputPortBindings().iterator(); Activity activity = binding.getBoundActivity(); Processor processor = binding.getBoundProcessor(); for (ProcessorInputPortBinding ip : iterable(inputBindings)) { if (!activity.getInputPorts().contains(ip.getBoundActivityPort())) { inputBindings.remove(); continue; } if (!processor.getInputPorts().contains(ip.getBoundProcessorPort())) { inputBindings.remove(); continue; } } Iterator<ProcessorOutputPortBinding> outputBindings = binding.getOutputPortBindings().iterator(); for (ProcessorOutputPortBinding op : iterable(outputBindings)) { if (!activity.getOutputPorts().contains(op.getBoundActivityPort())) { outputBindings.remove(); continue; } if (!processor.getOutputPorts().contains(op.getBoundProcessorPort())) { outputBindings.remove(); continue; } } } public void updateBindingByMatchingPorts(ProcessorBinding binding) { removePortsBindingForUnknownPorts(binding); bindActivityToProcessorByMatchingPorts(binding); } private <T> Iterable<T> iterable(final Iterator<T> it) { return new Iterable<T>() { @Override public Iterator<T> iterator() { return it; } }; } public static URI CONSTANT = URI.create("http://ns.taverna.org.uk/2010/activity/constant"); public static URI CONSTANT_CONFIG = CONSTANT.resolve("#Config"); public Processor createConstant(Workflow workflow, Profile profile, String name) { Processor processor = new Processor(null, name); workflow.getProcessors().addWithUniqueName(processor); processor.setParent(workflow); OutputProcessorPort valuePort = new OutputProcessorPort(processor, CONSTANT_VALUE_PORT); valuePort.setDepth(0); valuePort.setGranularDepth(0); Activity activity = createActivityFromProcessor(processor, profile); activity.setType(CONSTANT); createConfigurationFor(activity, CONSTANT_CONFIG); return processor; } public Configuration createConfigurationFor(Activity activity, URI configType) { Profile profile = activity.getParent(); Configuration config = new Configuration(activity.getName()); profile.getConfigurations().addWithUniqueName(config); config.setParent(profile); config.setConfigures(activity); config.setType(configType); return config; } public Configuration createConfigurationFor(Processor processor, Profile profile) { Configuration config = new Configuration(processor.getName() + "-proc"); profile.getConfigurations().addWithUniqueName(config); config.setParent(profile); config.setConfigures(processor); config.setType(Processor.CONFIG_TYPE); return config; } public void setConstantStringValue(Processor constant, String value, Profile profile) { Configuration config = configurationForActivityBoundToProcessor(constant, profile); config.getJsonAsObjectNode().put(CONSTANT_STRING, value); } public String getConstantStringValue(Processor constant, Profile profile) { Configuration config = configurationForActivityBoundToProcessor(constant, profile); return config.getJson().get(CONSTANT_STRING).asText(); } public Set<Processor> getConstants(Workflow workflow, Profile profile) { Set<Processor> procs = new LinkedHashSet<>(); for (Configuration config : profile.getConfigurations()) { Configurable configurable = config.getConfigures(); if (!CONSTANT.equals(configurable.getType()) || !(configurable instanceof Activity)) continue; for (ProcessorBinding bind : processorBindingsToActivity((Activity) configurable)) procs.add(bind.getBoundProcessor()); } return procs; } public void createDefaultIterationStrategyStack(Processor p) { p.setIterationStrategyStack(new IterationStrategyStack()); CrossProduct crossProduct = new CrossProduct(); for (InputProcessorPort in : p.getInputPorts()) { // As this is a NamedSet the above will always be in // the same alphabetical order // FIXME: What about different Locales? crossProduct.add(new PortNode(crossProduct, in)); } p.getIterationStrategyStack().add(crossProduct); } }