You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/02/23 11:06:03 UTC

[78/79] incubator-taverna-language git commit: validation moved into api

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/MismatchConfigurableTypeProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/MismatchConfigurableTypeProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/MismatchConfigurableTypeProblem.java
new file mode 100644
index 0000000..d39850d
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/MismatchConfigurableTypeProblem.java
@@ -0,0 +1,53 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.Configurable;
+import org.apache.taverna.scufl2.api.configurations.Configuration;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class MismatchConfigurableTypeProblem extends ValidationProblem {
+	private final Configurable configurable;
+
+	public MismatchConfigurableTypeProblem(Configuration configuration,
+			Configurable configurable) {
+		super(configuration);
+		this.configurable = configurable;
+	}
+
+	/**
+	 * @return the configurable
+	 */
+	public Configurable getConfigurable() {
+		return configurable;
+	}
+
+	@Override
+	public String toString() {
+		return "The types of " + getBean() + " and " + configurable
+				+ " are mismatched";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NegativeValueProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NegativeValueProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NegativeValueProblem.java
new file mode 100644
index 0000000..226edf4
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NegativeValueProblem.java
@@ -0,0 +1,60 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class NegativeValueProblem extends ValidationProblem {
+	private final String fieldName;
+	private final Integer fieldValue;
+
+	public NegativeValueProblem(WorkflowBean bean, String fieldName,
+			Integer fieldValue) {
+		super(bean);
+		this.fieldName = fieldName;
+		this.fieldValue = fieldValue;
+	}
+
+	/**
+	 * @return the fieldName
+	 */
+	public String getFieldName() {
+		return fieldName;
+	}
+
+	/**
+	 * @return the fieldValue
+	 */
+	public Integer getFieldValue() {
+		return fieldValue;
+	}
+
+	@Override
+	public String toString() {
+		return getBean() + " has " + fieldName + " of value " + fieldValue;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NonAbsoluteURIProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NonAbsoluteURIProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NonAbsoluteURIProblem.java
new file mode 100644
index 0000000..7aa54b2
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NonAbsoluteURIProblem.java
@@ -0,0 +1,63 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class NonAbsoluteURIProblem extends ValidationProblem {
+	private String fieldName;
+	private URI fieldValue;
+
+	public NonAbsoluteURIProblem(WorkflowBean bean, String fieldName,
+			URI fieldValue) {
+		super(bean);
+		this.fieldName = fieldName;
+		this.fieldValue = fieldValue;
+	}
+
+	/**
+	 * @return the fieldName
+	 */
+	public String getFieldName() {
+		return fieldName;
+	}
+
+	/**
+	 * @return the fieldValue
+	 */
+	public URI getFieldValue() {
+		return fieldValue;
+	}
+
+	@Override
+	public String toString() {
+		return getBean() + "has a non-absolute URI in field " + fieldName
+				+ " of value " + fieldValue;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NullFieldProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NullFieldProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NullFieldProblem.java
new file mode 100644
index 0000000..6c2801d
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/NullFieldProblem.java
@@ -0,0 +1,50 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class NullFieldProblem extends ValidationProblem {
+	private final String fieldName;
+
+	public NullFieldProblem(WorkflowBean bean, String fieldName) {
+		super(bean);
+		this.fieldName = fieldName;
+	}
+
+	/**
+	 * @return the fieldName
+	 */
+	public String getFieldName() {
+		return fieldName;
+	}
+
+	@Override
+	public String toString() {
+		return getBean() + " has a null " + fieldName;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/OutOfScopeValueProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/OutOfScopeValueProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/OutOfScopeValueProblem.java
new file mode 100644
index 0000000..7d6973d
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/OutOfScopeValueProblem.java
@@ -0,0 +1,60 @@
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class OutOfScopeValueProblem extends ValidationProblem {
+	private final String fieldName;
+	private final Object value;
+
+	public OutOfScopeValueProblem(WorkflowBean bean, String fieldName,
+			Object value) {
+		super(bean);
+		this.fieldName = fieldName;
+		this.value = value;
+	}
+
+	/**
+	 * @return the fieldName
+	 */
+	public String getFieldName() {
+		return fieldName;
+	}
+
+	/**
+	 * @return the value
+	 */
+	public Object getValue() {
+		return value;
+	}
+
+	@Override
+	public String toString() {
+		return getBean() + " has " + fieldName + " with out of scope value " + value;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMentionedTwiceProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMentionedTwiceProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMentionedTwiceProblem.java
new file mode 100644
index 0000000..af5f36c
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMentionedTwiceProblem.java
@@ -0,0 +1,51 @@
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyNode;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class PortMentionedTwiceProblem extends ValidationProblem {
+	private final IterationStrategyNode duplicateNode;
+
+	public PortMentionedTwiceProblem(IterationStrategyNode originalNode,
+			IterationStrategyNode duplicateNode) {
+		super(originalNode);
+		this.duplicateNode = duplicateNode;
+	}
+
+	/**
+	 * @return the iterationStrategyNode
+	 */
+	public IterationStrategyNode getDuplicateNode() {
+		return duplicateNode;
+	}
+
+	@Override
+	public String toString() {
+		return (getBean() + " and " + duplicateNode + " reference the same port");
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMissingFromIterationStrategyStackProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMissingFromIterationStrategyStackProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMissingFromIterationStrategyStackProblem.java
new file mode 100644
index 0000000..502a023
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/PortMissingFromIterationStrategyStackProblem.java
@@ -0,0 +1,54 @@
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyStack;
+import org.apache.taverna.scufl2.api.port.Port;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ * 
+ */
+public class PortMissingFromIterationStrategyStackProblem extends
+		ValidationProblem {
+	private final Port port;
+
+	public PortMissingFromIterationStrategyStackProblem(Port port,
+			IterationStrategyStack iterationStrategyStack) {
+		super(iterationStrategyStack);
+		this.port = port;
+	}
+
+	/**
+	 * @return the port
+	 */
+	public Port getPort() {
+		return port;
+	}
+
+	@Override
+	public String toString() {
+		return getBean() + " does not include " + port;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/WrongParentProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/WrongParentProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/WrongParentProblem.java
new file mode 100644
index 0000000..bb4b40e
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/correctness/report/WrongParentProblem.java
@@ -0,0 +1,40 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class WrongParentProblem extends ValidationProblem {
+	public WrongParentProblem(WorkflowBean bean) {
+		super(bean);
+	}
+
+	@Override
+	public String toString() {
+		return getBean() + " does not have the correct parent";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/DefaultStructuralValidationListener.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/DefaultStructuralValidationListener.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/DefaultStructuralValidationListener.java
new file mode 100644
index 0000000..1f1f9c9
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/DefaultStructuralValidationListener.java
@@ -0,0 +1,110 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+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.DotProduct;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyNode;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+import org.apache.taverna.scufl2.validation.ValidationException;
+
+
+/**
+ * @author alanrw
+ */
+public class DefaultStructuralValidationListener implements
+		StructuralValidationListener {
+	@Override
+	public void dataLinkReceiver(DataLink dl) {
+	}
+
+	@Override
+	public void dataLinkSender(DataLink dl) {
+	}
+
+	@Override
+	public void depthResolution(WorkflowBean owp, Integer portResolvedDepth) {
+	}
+
+	@Override
+	public void dotProductIterationMismatch(DotProduct dotProduct) {
+	}
+
+	@Override
+	public void emptyCrossProduct(CrossProduct crossProduct) {
+	}
+
+	@Override
+	public void emptyDotProduct(DotProduct dotProduct) {
+	}
+
+	@Override
+	public void failedProcessorAdded(Processor p) {
+	}
+
+	@Override
+	public void incompleteWorkflow(Workflow w) {
+	}
+
+	@Override
+	public void missingIterationStrategyStack(Processor p) {
+	}
+
+	@Override
+	public void missingMainIncomingLink(ReceiverPort owp) {
+	}
+
+	@Override
+	public void passedProcessor(Processor p) {
+	}
+
+	@Override
+	public void unrecognizedIterationStrategyNode(
+			IterationStrategyNode iterationStrategyNode) {
+	}
+
+	@Override
+	public void unresolvedOutput(OutputWorkflowPort owp) {
+	}
+
+	@Override
+	public void unresolvedProcessorAdded(Processor p) {
+	}
+
+	@Override
+	public boolean detectedProblems() {
+		return false;
+	}
+
+	@Override
+	public ValidationException getException() {
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ReportStructuralValidationListener.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ReportStructuralValidationListener.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ReportStructuralValidationListener.java
new file mode 100644
index 0000000..60ead09
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ReportStructuralValidationListener.java
@@ -0,0 +1,236 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural;
+/*
+ *
+ * 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.taverna.scufl2.api.common.WorkflowBean;
+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.DotProduct;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyNode;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+import org.apache.taverna.scufl2.api.port.SenderPort;
+import org.apache.taverna.scufl2.validation.ValidationException;
+
+
+/**
+ * @author alanrw
+ */
+public class ReportStructuralValidationListener extends
+		DefaultStructuralValidationListener {
+	private Map<SenderPort, List<DataLink>> senderDataLinkMap = new HashMap<>();
+	private Map<ReceiverPort, List<DataLink>> receiverDataLinkMap = new HashMap<>();
+	private Map<WorkflowBean, Integer> resolvedDepthMap = new HashMap<>();
+	private Set<DotProduct> dotProductIterationMismatches = new HashSet<>();
+	private Set<CrossProduct> emptyCrossProducts = new HashSet<>();
+	private Set<DotProduct> emptyDotProducts = new HashSet<>();
+	private Set<Processor> failedProcessors = new HashSet<>();
+	private Set<Workflow> incompleteWorkflows = new HashSet<>();
+	private Set<Processor> missingIterationStrategyStacks = new HashSet<>();
+	private Set<ReceiverPort> missingMainIncomingDataLinks = new HashSet<>();
+	private Set<Processor> passedProcessors = new HashSet<>();
+	private Set<IterationStrategyNode> unrecognizedIterationStrategyNodes = new HashSet<>();
+	private Set<OutputWorkflowPort> unresolvedOutputs = new HashSet<>();
+	private Set<Processor> unresolvedProcessors = new HashSet<>();
+
+	@Override
+	public void dataLinkReceiver(DataLink dl) {
+		ReceiverPort receiver = dl.getSendsTo();
+		if (receiver != null) {
+			if (!receiverDataLinkMap.containsKey(receiver))
+				receiverDataLinkMap.put(receiver, new ArrayList<DataLink>());
+			receiverDataLinkMap.get(receiver).add(dl);
+		}
+	}
+
+	@Override
+	public void dataLinkSender(DataLink dl) {
+		SenderPort sender = dl.getReceivesFrom();
+		if (sender != null) {
+			if (!senderDataLinkMap.containsKey(sender))
+				senderDataLinkMap.put(sender, new ArrayList<DataLink>());
+			senderDataLinkMap.get(sender).add(dl);
+		}
+	}
+	
+	@Override
+	public void depthResolution(WorkflowBean owp, Integer portResolvedDepth) {
+		resolvedDepthMap.put(owp, portResolvedDepth);
+	}
+
+	@Override
+	public void dotProductIterationMismatch(DotProduct dotProduct) {
+		dotProductIterationMismatches.add(dotProduct);
+	}
+
+	@Override
+	public void emptyCrossProduct(CrossProduct crossProduct) {
+		emptyCrossProducts.add(crossProduct);
+	}
+
+	@Override
+	public void emptyDotProduct(DotProduct dotProduct) {
+		emptyDotProducts.add(dotProduct);
+	}
+
+	@Override
+	public void failedProcessorAdded(Processor p) {
+		failedProcessors.add(p);
+	}
+
+	@Override
+	public void incompleteWorkflow(Workflow w) {
+		incompleteWorkflows.add(w);
+	}
+	
+	/**
+	 * @return
+	 */
+	public Set<Workflow> getIncompleteWorkflows() {
+		return incompleteWorkflows;
+	}
+
+	@Override
+	public void missingIterationStrategyStack(Processor p) {
+		missingIterationStrategyStacks.add(p);
+	}
+
+	@Override
+	public void missingMainIncomingLink(ReceiverPort owp) {
+		missingMainIncomingDataLinks.add(owp);
+	}
+
+	@Override
+	public void passedProcessor(Processor p) {
+		passedProcessors.add(p);
+	}
+
+	@Override
+	public void unrecognizedIterationStrategyNode(
+			IterationStrategyNode iterationStrategyNode) {
+		unrecognizedIterationStrategyNodes.add(iterationStrategyNode);
+	}
+
+	@Override
+	public void unresolvedOutput(OutputWorkflowPort owp) {
+		unresolvedOutputs.add(owp);
+	}
+
+	@Override
+	public void unresolvedProcessorAdded(Processor p) {
+		unresolvedProcessors.add(p);
+	}
+
+	/**
+	 * @return the dotProductIterationMismatches
+	 */
+	public Set<DotProduct> getDotProductIterationMismatches() {
+		return dotProductIterationMismatches;
+	}
+
+	/**
+	 * @return the emptyCrossProducts
+	 */
+	public Set<CrossProduct> getEmptyCrossProducts() {
+		return emptyCrossProducts;
+	}
+
+	/**
+	 * @return the emptyDotProducts
+	 */
+	public Set<DotProduct> getEmptyDotProducts() {
+		return emptyDotProducts;
+	}
+
+	/**
+	 * @return the failedProcessors
+	 */
+	public Set<Processor> getFailedProcessors() {
+		return failedProcessors;
+	}
+
+	/**
+	 * @return the missingIterationStrategyStacks
+	 */
+	public Set<Processor> getMissingIterationStrategyStacks() {
+		return missingIterationStrategyStacks;
+	}
+
+	/**
+	 * @return the missingMainIncomingDataLinks
+	 */
+	public Set<ReceiverPort> getMissingMainIncomingDataLinks() {
+		return missingMainIncomingDataLinks;
+	}
+
+	/**
+	 * @return the unrecognizedIterationStrategyNodes
+	 */
+	public Set<IterationStrategyNode> getUnrecognizedIterationStrategyNodes() {
+		return unrecognizedIterationStrategyNodes;
+	}
+
+	/**
+	 * @return the unresolvedOutputs
+	 */
+	public Set<OutputWorkflowPort> getUnresolvedOutputs() {
+		return unresolvedOutputs;
+	}
+
+	/**
+	 * @return the unresolvedProcessors
+	 */
+	public Set<Processor> getUnresolvedProcessors() {
+		return unresolvedProcessors;
+	}
+	
+	@Override
+	public boolean detectedProblems() {
+		return !(dotProductIterationMismatches.isEmpty()
+				&& emptyCrossProducts.isEmpty() && emptyDotProducts.isEmpty()
+				&& failedProcessors.isEmpty() && incompleteWorkflows.isEmpty()
+				&& missingIterationStrategyStacks.isEmpty()
+				&& missingMainIncomingDataLinks.isEmpty()
+				&& unrecognizedIterationStrategyNodes.isEmpty()
+				&& unresolvedOutputs.isEmpty() && unresolvedProcessors
+					.isEmpty());
+	}	
+
+	@Override
+	public ValidationException getException() {
+		if (!detectedProblems())
+			return null;
+		return new ValidationException(this.toString());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidationListener.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidationListener.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidationListener.java
new file mode 100644
index 0000000..a6dd898
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidationListener.java
@@ -0,0 +1,65 @@
+package org.apache.taverna.scufl2.validation.structural;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+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.DotProduct;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyNode;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+import org.apache.taverna.scufl2.validation.ValidationReport;
+
+
+public interface StructuralValidationListener extends ValidationReport {
+	void passedProcessor(Processor p);
+
+	void failedProcessorAdded(Processor p);
+
+	void unresolvedProcessorAdded(Processor p);
+
+	void missingMainIncomingLink(ReceiverPort owp);
+
+	void unresolvedOutput(OutputWorkflowPort owp);
+
+	void depthResolution(WorkflowBean owp, Integer portResolvedDepth);
+
+	void missingIterationStrategyStack(Processor p);
+
+	void emptyDotProduct(DotProduct dotProduct);
+
+	void dotProductIterationMismatch(DotProduct dotProduct);
+
+	void emptyCrossProduct(CrossProduct crossProduct);
+
+	void dataLinkSender(DataLink dl);
+
+	void dataLinkReceiver(DataLink dl);
+
+	void unrecognizedIterationStrategyNode(
+			IterationStrategyNode iterationStrategyNode);
+
+	void incompleteWorkflow(Workflow w);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidator.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidator.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidator.java
new file mode 100644
index 0000000..7ed5978
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/StructuralValidator.java
@@ -0,0 +1,412 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural;
+/*
+ *
+ * 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+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.DotProduct;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyNode;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyStack;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode;
+import org.apache.taverna.scufl2.api.iterationstrategy.PortNode;
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.validation.Validator;
+
+
+/**
+ * @author alanrw
+ */
+public final class StructuralValidator implements
+		Validator<StructuralValidationListener> {
+	private static enum ProcessorCheckStatus {
+		COULD_NOT_CHECK, PASSED, FAILED
+	};
+
+	protected ThreadLocal<ValidatorState> validatorState = new ThreadLocal<ValidatorState>() {
+		@Override
+		protected ValidatorState initialValue() {
+			return new ValidatorState();
+		};
+	};
+
+	public void checkStructure(WorkflowBundle bundle,
+			StructuralValidationListener eventListener) {
+		validatorState.get().setEventListener(eventListener);
+		validatorState.get().setWorkflowBundle(bundle);
+		for (Workflow w : bundle.getWorkflows())
+			checkStructure(w);
+	}
+
+	public ValidatorState getValidatorState() {
+		return validatorState.get();
+	}
+
+	public void checkStructure(Workflow workflow,
+			StructuralValidationListener eventListener) {
+		validatorState.get().setEventListener(eventListener);
+		checkStructure(workflow);
+	}
+
+	private void checkStructure(Workflow workflow) {
+		validatorState.get().setWorkflow(workflow);
+		validateWorkflow();
+	}
+
+	private void validateWorkflow() {
+		clearWorkflowData();
+		rememberDataLinkConnections();
+		inheritDataLinkDepthsFromWorkflowInputPorts();
+		checkProcessors();
+		checkWorkflowOutputPorts();
+		checkCompleteness();
+	}
+
+	private void clearWorkflowData() {
+		validatorState.get().clearWorkflowData();
+	}
+
+	private void checkCompleteness() {
+		Workflow w = validatorState.get().getWorkflow();
+		if (w.getProcessors().isEmpty() && w.getOutputPorts().isEmpty()) {
+			validatorState.get().getEventListener().incompleteWorkflow(w);
+			// validatorState.get().addIncompleteWorkflow(w);
+		}
+	}
+
+	private void checkProcessors() {
+		Workflow workflow = validatorState.get().getWorkflow();
+		List<Processor> failedProcessors = new ArrayList<>();
+		List<Processor> unresolvedProcessors = new ArrayList<>();
+		unresolvedProcessors.addAll(workflow.getProcessors());
+
+		boolean finished = false;
+		while (!finished) {
+			// We're finished unless something happens later
+			finished = true;
+			/*
+			 * Keep a list of processors to remove from the unresolved list
+			 * because they've been resolved properly
+			 */
+			List<Processor> removeValidated = new ArrayList<>();
+			// Keep another list of those that have failed
+			List<Processor> removeFailed = new ArrayList<>();
+
+			for (Processor p : unresolvedProcessors) {
+				validatorState.get().setProcessor(p);
+				ProcessorCheckStatus entityValid = checkProcessor();
+				switch (entityValid) {
+				case PASSED:
+					validatorState.get().getEventListener().passedProcessor(p);
+					// validatorState.get().addPassedProcessor(p);
+					removeValidated.add(p);
+					break;
+				case FAILED:
+					validatorState.get().getEventListener()
+							.failedProcessorAdded(p);
+					// validatorState.get().failCurrentProcessor();
+					removeFailed.add(p);
+					break;
+				case COULD_NOT_CHECK:
+					break;
+				}
+			}
+
+			/*
+			 * Remove validated and failed items from the pending lists. If
+			 * anything was removed because it validated okay then we're not
+			 * finished yet and should reset the boolean finished flag
+			 */
+			if (!removeValidated.isEmpty()) {
+				unresolvedProcessors.removeAll(removeValidated);
+				finished = false;
+			}
+			unresolvedProcessors.removeAll(failedProcessors);
+		}
+		for (Processor p : unresolvedProcessors)
+			validatorState.get().getEventListener().unresolvedProcessorAdded(p);
+		// validatorState.get().addUnresolvedProcessors(unresolvedProcessors);
+	}
+
+	private void checkWorkflowOutputPorts() {
+		for (OutputWorkflowPort owp : validatorState.get().getWorkflow()
+				.getOutputPorts()) {
+			DataLink mainIncomingLink = validatorState.get()
+					.getMainIncomingDataLink(owp);
+			if (mainIncomingLink == null) {
+				validatorState.get().getEventListener()
+						.missingMainIncomingLink(owp);
+				// validatorState.get().addMissingMainIncomingDataLink(owp);
+			}
+			Integer dataLinkResolvedDepth = validatorState.get()
+					.getDataLinkResolvedDepth(mainIncomingLink);
+			if (dataLinkResolvedDepth == null) {
+				validatorState.get().getEventListener().unresolvedOutput(owp);
+				// validatorState.get().addUnresolvedOutput(owp);
+				return;
+			}
+
+			// int granularDepth =
+			// mainIncomingLink.getSource().getGranularDepth();
+			Integer portResolvedDepth = dataLinkResolvedDepth
+					+ (validatorState.get().isMergedPort(owp) ? 1 : 0);
+			validatorState.get().getEventListener()
+					.depthResolution(owp, portResolvedDepth);
+			validatorState.get().setPortResolvedDepth(owp, portResolvedDepth);
+			// dopi.setDepths(resolvedDepth, granularDepth);
+		}
+	}
+
+	private ProcessorCheckStatus checkProcessor() {
+		Processor p = validatorState.get().getProcessor();
+		Map<InputProcessorPort, Integer> inputDepths = new HashMap<>();
+		// Check whether all our input ports have inbound links
+		for (InputProcessorPort input : p.getInputPorts()) {
+			DataLink mainIncomingLink = validatorState.get()
+					.getMainIncomingDataLink(input);
+			if (mainIncomingLink == null) {
+				validatorState.get().getEventListener()
+						.missingMainIncomingLink(input);
+				// validatorState.get().addMissingMainIncomingDataLink(input);
+				return ProcessorCheckStatus.FAILED;
+			}
+			Integer dataLinkResolvedDepth = validatorState.get()
+					.getDataLinkResolvedDepth(mainIncomingLink);
+			if (dataLinkResolvedDepth == null)
+				return ProcessorCheckStatus.COULD_NOT_CHECK;
+
+			Integer resolvedDepth = dataLinkResolvedDepth
+					+ (validatorState.get().isMergedPort(input) ? 1 : 0);
+			validatorState.get().getEventListener()
+					.depthResolution(input, resolvedDepth);
+			validatorState.get().setPortResolvedDepth(input, resolvedDepth);
+			inputDepths.put(input, resolvedDepth);
+		}
+
+		Integer resultWrappingDepth = calculateResultWrappingDepth(inputDepths);
+		if (resultWrappingDepth == null)
+			return ProcessorCheckStatus.FAILED;
+
+		for (OutputProcessorPort output : p.getOutputPorts()) {
+			Integer portDepth = output.getDepth();
+			Integer resolvedDepth = portDepth + resultWrappingDepth;
+			validatorState.get().getEventListener()
+					.depthResolution(output, resolvedDepth);
+			validatorState.get().setPortResolvedDepth(output, resolvedDepth);
+			for (DataLink dl : validatorState.get()
+					.getOutgoingDataLinks(output)) {
+				validatorState.get().getEventListener()
+						.depthResolution(dl, resolvedDepth);
+				validatorState.get()
+						.setDataLinkResolvedDepth(dl, resolvedDepth);
+			}
+		}
+
+		return ProcessorCheckStatus.PASSED;
+	}
+
+	Integer calculateResultWrappingDepth(
+			Map<InputProcessorPort, Integer> inputDepths) {
+		Processor p = validatorState.get().getProcessor();
+		IterationStrategyStack iss = p.getIterationStrategyStack();
+		if (iss == null) {
+			validatorState.get().getEventListener()
+					.missingIterationStrategyStack(p);
+			// validatorState.get().addMissingIterationStrategyStack(p);
+			validatorState.get().getEventListener().failedProcessorAdded(p);
+			// validatorState.get().failCurrentProcessor();
+			return null;
+		}
+
+		if (iss.isEmpty())
+			return 0;
+		IterationStrategyTopNode iterationStrategyTopNode = iss.get(0);
+		Integer depth = getIterationDepth(iterationStrategyTopNode, inputDepths);
+		if (depth == null)
+			return null;
+		IterationStrategyTopNode previousNode = iterationStrategyTopNode;
+		for (int index = 1; index < iss.size(); index++) {
+			/*
+			 * Construct the input depths for the staged iteration strategies
+			 * after the first one by looking at the previous iteration
+			 * strategy's desired cardinalities on its input ports.
+			 */
+			Map<InputProcessorPort, Integer> stagedInputDepths = getDesiredCardinalities(previousNode);
+			iterationStrategyTopNode = iss.get(index);
+			Integer nodeDepth = getIterationDepth(iterationStrategyTopNode,
+					stagedInputDepths);
+			if (nodeDepth == null)
+				return null;
+			depth += nodeDepth;
+			previousNode = iterationStrategyTopNode;
+		}
+		return depth;
+	}
+
+	private Map<InputProcessorPort, Integer> getDesiredCardinalities(
+			IterationStrategyTopNode iterationStrategyTopNode) {
+		Map<InputProcessorPort, Integer> desiredCardinalities = new HashMap<>();
+		fillInDesiredCardinalities(iterationStrategyTopNode,
+				desiredCardinalities);
+		return desiredCardinalities;
+	}
+
+	private void fillInDesiredCardinalities(
+			IterationStrategyNode iterationStrategyNode,
+			Map<InputProcessorPort, Integer> desiredCardinalities) {
+		if (iterationStrategyNode instanceof IterationStrategyTopNode)
+			for (IterationStrategyNode subNode : (IterationStrategyTopNode) iterationStrategyNode)
+				fillInDesiredCardinalities(subNode, desiredCardinalities);
+		else if (iterationStrategyNode instanceof PortNode) {
+			PortNode portNode = (PortNode) iterationStrategyNode;
+			desiredCardinalities.put(portNode.getInputProcessorPort(),
+					portNode.getDesiredDepth());
+		}
+	}
+
+	public Integer getIterationDepth(
+			IterationStrategyNode iterationStrategyNode,
+			Map<InputProcessorPort, Integer> inputDepths) {
+		if (iterationStrategyNode instanceof CrossProduct)
+			return getCrossProductIterationDepth(
+					(CrossProduct) iterationStrategyNode, inputDepths);
+		if (iterationStrategyNode instanceof DotProduct)
+			return getDotProductIterationDepth(
+					(DotProduct) iterationStrategyNode, inputDepths);
+		if (iterationStrategyNode instanceof PortNode)
+			return getPortNodeIterationDepth((PortNode) iterationStrategyNode,
+					inputDepths);
+		validatorState.get().getEventListener()
+				.unrecognizedIterationStrategyNode(iterationStrategyNode);
+		// validatorState.get().addUnrecognizedIterationStrategyNode(iterationStrategyNode);
+		validatorState.get().getEventListener()
+				.failedProcessorAdded(validatorState.get().getProcessor());
+		// validatorState.get().failCurrentProcessor();
+		return null;
+	}
+
+	private Integer getPortNodeIterationDepth(PortNode portNode,
+			Map<InputProcessorPort, Integer> inputDepths) {
+		int myInputDepth = inputDepths.get(portNode.getInputProcessorPort());
+		int depthMismatch = myInputDepth - portNode.getDesiredDepth();
+		return (depthMismatch > 0 ? depthMismatch : 0);
+	}
+
+	public Integer getDotProductIterationDepth(DotProduct dotProduct,
+			Map<InputProcessorPort, Integer> inputDepths) {
+		if (dotProduct.isEmpty()) {
+			validatorState.get().getEventListener().emptyDotProduct(dotProduct);
+			// validatorState.get().addEmptyDotProduct(dotProduct);
+			validatorState.get().getEventListener()
+					.failedProcessorAdded(validatorState.get().getProcessor());
+			// validatorState.get().failCurrentProcessor();
+			return null;
+		}
+		Integer depth = getIterationDepth(dotProduct.get(0), inputDepths);
+		if (depth == null)
+			return null;
+		for (IterationStrategyNode childNode : dotProduct) {
+			Integer childNodeDepth = getIterationDepth(childNode, inputDepths);
+			if (childNodeDepth == null)
+				return null;
+			if (!childNodeDepth.equals(depth)) {
+				validatorState.get().getEventListener()
+						.dotProductIterationMismatch(dotProduct);
+				// validatorState.get().addDotProductIterationMismatch(dotProduct);
+				validatorState
+						.get()
+						.getEventListener()
+						.failedProcessorAdded(
+								validatorState.get().getProcessor());
+				// validatorState.get().failCurrentProcessor();
+				return null;
+			}
+		}
+		return depth;
+	}
+
+	private Integer getCrossProductIterationDepth(CrossProduct crossProduct,
+			Map<InputProcessorPort, Integer> inputDepths) {
+		if (crossProduct.isEmpty()) {
+			validatorState.get().getEventListener()
+					.emptyCrossProduct(crossProduct);
+			// validatorState.get().addEmptyCrossProduct(crossProduct);
+			validatorState.get().getEventListener()
+					.failedProcessorAdded(validatorState.get().getProcessor());
+			// validatorState.get().failCurrentProcessor();
+			return null;
+		}
+		int temp = 0;
+		for (IterationStrategyNode child : crossProduct) {
+			Integer childNodeDepth = getIterationDepth(child, inputDepths);
+			if (childNodeDepth == null)
+				return null;
+			temp += childNodeDepth;
+		}
+		return temp;
+	}
+
+	private void rememberDataLinkConnections() {
+		Workflow workflow = validatorState.get().getWorkflow();
+		for (DataLink dl : workflow.getDataLinks()) {
+			validatorState.get().getEventListener().dataLinkSender(dl);
+			validatorState.get().rememberDataLinkSender(dl);
+			validatorState.get().getEventListener().dataLinkReceiver(dl);
+			validatorState.get().rememberDataLinkReceiver(dl);
+		}
+	}
+
+	private void inheritDataLinkDepthsFromWorkflowInputPorts() {
+		Workflow workflow = validatorState.get().getWorkflow();
+		for (InputWorkflowPort iwp : workflow.getInputPorts()) {
+			Integer iwpDepth = iwp.getDepth();
+			validatorState.get().getEventListener()
+					.depthResolution(iwp, iwpDepth);
+			validatorState.get().setPortResolvedDepth(iwp, iwpDepth);
+			for (DataLink dl : validatorState.get().getOutgoingDataLinks(iwp)) {
+				validatorState.get().getEventListener()
+						.depthResolution(dl, iwpDepth);
+				validatorState.get().setDataLinkResolvedDepth(dl, iwpDepth);
+			}
+		}
+	}
+
+	@Override
+	public StructuralValidationListener validate(WorkflowBundle workflowBundle) {
+		StructuralValidationListener l = new ReportStructuralValidationListener();
+		this.checkStructure(workflowBundle, l);
+		return l;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ValidatorState.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ValidatorState.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ValidatorState.java
new file mode 100644
index 0000000..66bec9a
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/ValidatorState.java
@@ -0,0 +1,184 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural;
+/*
+ *
+ * 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 static java.util.Collections.emptyList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+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.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.Port;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+import org.apache.taverna.scufl2.api.port.SenderPort;
+import org.apache.taverna.scufl2.api.profiles.Profile;
+
+
+/**
+ * @author alanrw
+ */
+public class ValidatorState {
+	private WorkflowBundle workflowBundle;
+	private Workflow workflow;
+	private Profile profile;
+	private Processor processor;
+	private Map<DataLink, Integer> dataLinkResolvedDepthMap = new HashMap<>();
+	private Map<SenderPort, List<DataLink>> senderDataLinkMap = new HashMap<>();
+	private Map<ReceiverPort, List<DataLink>> receiverDataLinkMap = new HashMap<>();
+	private Map<Port, Integer> portResolvedDepthMap = new HashMap<>();
+	private StructuralValidationListener eventListener = new DefaultStructuralValidationListener();
+
+	public void setWorkflowBundle(WorkflowBundle workflowBundle) {
+		this.workflowBundle = workflowBundle;
+	}
+
+	public void setWorkflow(Workflow workflow) {
+		this.workflow = workflow;
+	}
+
+	public WorkflowBundle getWorkflowBundle() {
+		return workflowBundle;
+	}
+
+	public Workflow getWorkflow() {
+		return workflow;
+	}
+
+	public Profile getProfile() {
+		return profile;
+	}
+
+	public void setDataLinkResolvedDepth(DataLink dl, Integer i) {
+		dataLinkResolvedDepthMap.put(dl, i);
+	}
+
+	public Integer getDataLinkResolvedDepth(DataLink dl) {
+		return dataLinkResolvedDepthMap.get(dl);
+	}
+
+	public void rememberDataLinkSender(DataLink dl) {
+		SenderPort sender = dl.getReceivesFrom();
+		if (sender != null) {
+			if (!senderDataLinkMap.containsKey(sender))
+				senderDataLinkMap.put(sender, new ArrayList<DataLink>());
+			senderDataLinkMap.get(sender).add(dl);
+		}
+	}
+
+	public void rememberDataLinkReceiver(DataLink dl) {
+		ReceiverPort receiver = dl.getSendsTo();
+		if (receiver != null) {
+			if (!receiverDataLinkMap.containsKey(receiver))
+				receiverDataLinkMap.put(receiver, new ArrayList<DataLink>());
+			receiverDataLinkMap.get(receiver).add(dl);
+		}
+	}
+
+	public List<DataLink> getOutgoingDataLinks(SenderPort iwp) {
+		List<DataLink> result = senderDataLinkMap.get(iwp);
+		if (result == null)
+			result = emptyList();
+		return result;
+	}
+
+	public List<DataLink> getIncomingDataLinks(ReceiverPort rp) {
+		List<DataLink> result = receiverDataLinkMap.get(rp);
+		if (result == null)
+			result = emptyList();
+		return result;
+	}
+
+	public DataLink getMainIncomingDataLink(ReceiverPort rp) {
+		List<DataLink> incomingLinks = getIncomingDataLinks(rp);
+		if (incomingLinks.isEmpty())
+			return null;
+		if (incomingLinks.size() == 1)
+			return incomingLinks.get(0);
+		for (DataLink dl : incomingLinks)
+			if (dl.getMergePosition() == 0)
+				return dl;
+		return null;
+	}
+
+	public boolean isMergedPort(ReceiverPort rp) {
+		return getIncomingDataLinks(rp).size() > 1;
+	}
+
+	public void setPortResolvedDepth(Port owp, Integer i) {
+		portResolvedDepthMap.put(owp, i);
+	}
+
+	public Integer getPortResolvedDepth(Port p) {
+		return portResolvedDepthMap.get(p);
+	}
+
+	public void setProcessor(Processor p) {
+		this.processor = p;
+	}
+
+	public Processor getProcessor() {
+		return this.processor;
+	}
+
+	public void setEventListener(StructuralValidationListener eventListener) {
+		this.eventListener = eventListener;
+	}
+
+	public StructuralValidationListener getEventListener() {
+		return eventListener;
+	}
+
+	public void clearWorkflowData() {
+		for (DataLink dl : workflow.getDataLinks())
+			dataLinkResolvedDepthMap.remove(dl);
+		for (InputWorkflowPort iwp : workflow.getInputPorts()) {
+			senderDataLinkMap.remove(iwp);
+			portResolvedDepthMap.remove(iwp);
+		}
+		for (Processor p : workflow.getProcessors()) {
+			for (InputProcessorPort ipp : p.getInputPorts()) {
+				portResolvedDepthMap.remove(ipp);
+				receiverDataLinkMap.remove(ipp);
+			}
+			for (OutputProcessorPort opp : p.getOutputPorts()) {
+				portResolvedDepthMap.remove(opp);
+				senderDataLinkMap.remove(opp);
+			}
+		}
+		for (OutputWorkflowPort owp : workflow.getOutputPorts()) {
+			portResolvedDepthMap.remove(owp);
+			receiverDataLinkMap.remove(owp);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/DotProductIterationMismatchProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/DotProductIterationMismatchProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/DotProductIterationMismatchProblem.java
new file mode 100644
index 0000000..0edcec4
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/DotProductIterationMismatchProblem.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class DotProductIterationMismatchProblem extends ValidationProblem {
+	public DotProductIterationMismatchProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyCrossProductProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyCrossProductProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyCrossProductProblem.java
new file mode 100644
index 0000000..f22520a
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyCrossProductProblem.java
@@ -0,0 +1,32 @@
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class EmptyCrossProductProblem extends ValidationProblem {
+	public EmptyCrossProductProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyDotProductProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyDotProductProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyDotProductProblem.java
new file mode 100644
index 0000000..31d5554
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/EmptyDotProductProblem.java
@@ -0,0 +1,32 @@
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class EmptyDotProductProblem extends ValidationProblem {
+	public EmptyDotProductProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/FailedProcessorProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/FailedProcessorProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/FailedProcessorProblem.java
new file mode 100644
index 0000000..ad6a240
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/FailedProcessorProblem.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class FailedProcessorProblem extends ValidationProblem {
+	public FailedProcessorProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/IncompleteWorkflowProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/IncompleteWorkflowProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/IncompleteWorkflowProblem.java
new file mode 100644
index 0000000..f58174f
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/IncompleteWorkflowProblem.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class IncompleteWorkflowProblem extends ValidationProblem {
+	public IncompleteWorkflowProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingIterationStrategyStackProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingIterationStrategyStackProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingIterationStrategyStackProblem.java
new file mode 100644
index 0000000..d1e97ce
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingIterationStrategyStackProblem.java
@@ -0,0 +1,32 @@
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+public class MissingIterationStrategyStackProblem extends ValidationProblem {
+	public MissingIterationStrategyStackProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingMainIncomingDataLinkProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingMainIncomingDataLinkProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingMainIncomingDataLinkProblem.java
new file mode 100644
index 0000000..24a256f
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/MissingMainIncomingDataLinkProblem.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class MissingMainIncomingDataLinkProblem extends ValidationProblem {
+	public MissingMainIncomingDataLinkProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnrecognizedIterationStrategyNodeProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnrecognizedIterationStrategyNodeProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnrecognizedIterationStrategyNodeProblem.java
new file mode 100644
index 0000000..9a42082
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnrecognizedIterationStrategyNodeProblem.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class UnrecognizedIterationStrategyNodeProblem extends ValidationProblem {
+	public UnrecognizedIterationStrategyNodeProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedOutputProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedOutputProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedOutputProblem.java
new file mode 100644
index 0000000..00c8f4b
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedOutputProblem.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class UnresolvedOutputProblem extends ValidationProblem {
+	public UnresolvedOutputProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedProcessorProblem.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedProcessorProblem.java b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedProcessorProblem.java
new file mode 100644
index 0000000..4fa89e4
--- /dev/null
+++ b/taverna-scufl2-api/src/main/java/org/apache/taverna/scufl2/validation/structural/report/UnresolvedProcessorProblem.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.structural.report;
+/*
+ *
+ * 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 org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.validation.ValidationProblem;
+
+
+/**
+ * @author alanrw
+ */
+public class UnresolvedProcessorProblem extends ValidationProblem {
+	public UnresolvedProcessorProblem(WorkflowBean bean) {
+		super(bean);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyProfile.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyProfile.java b/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyProfile.java
new file mode 100644
index 0000000..2e0f630
--- /dev/null
+++ b/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyProfile.java
@@ -0,0 +1,117 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness;
+/*
+ *
+ * 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.util.ArrayList;
+import java.util.List;
+
+import org.apache.taverna.scufl2.api.activity.Activity;
+import org.apache.taverna.scufl2.api.common.NamedSet;
+import org.apache.taverna.scufl2.api.common.Visitor;
+import org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.api.configurations.Configuration;
+import org.apache.taverna.scufl2.api.profiles.ProcessorBinding;
+import org.apache.taverna.scufl2.api.profiles.Profile;
+
+
+class DummyProfile extends Profile {
+
+	private NamedSet<ProcessorBinding> processorBindings = null;
+
+	private NamedSet<Configuration> configurations = null;
+
+	private NamedSet<Activity> activities = null;
+
+	/**
+	 * @return the processorBindings
+	 */
+	@Override
+	public NamedSet<ProcessorBinding> getProcessorBindings() {
+		return processorBindings;
+	}
+
+	/**
+	 * @param processorBindings the processorBindings to set
+	 */
+	public void setProcessorBindings(NamedSet<ProcessorBinding> processorBindings) {
+		this.processorBindings = processorBindings;
+	}
+
+	/**
+	 * @return the configurations
+	 */
+	@Override
+	public NamedSet<Configuration> getConfigurations() {
+		return configurations;
+	}
+
+	/**
+	 * @param configurations the configurations to set
+	 */
+	public void setConfigurations(NamedSet<Configuration> configurations) {
+		this.configurations = configurations;
+	}
+
+	/**
+	 * @return the activities
+	 */
+	@Override
+	public NamedSet<Activity> getActivities() {
+		return activities;
+	}
+
+	/**
+	 * @param activities the activities to set
+	 */
+	public void setActivities(NamedSet<Activity> activities) {
+		this.activities = activities;
+	}
+	
+	@Override
+	public boolean accept(Visitor visitor) {
+		if (visitor.visitEnter(this)) {
+			List<Iterable<? extends WorkflowBean>> children = new ArrayList<Iterable<? extends WorkflowBean>>();
+			if (getActivities() != null) {
+				children.add(getActivities());
+			}
+			if (getProcessorBindings() != null) {
+				children.add(getProcessorBindings());
+			}
+			if (getConfigurations() != null) {
+				children.add(getConfigurations());
+			}
+			outer: for (Iterable<? extends WorkflowBean> it : children) {
+				for (WorkflowBean bean : it) {
+					if (!bean.accept(visitor)) {
+						break outer;
+					}
+				}
+			}
+		}
+		return visitor.visitLeave(this);
+	}
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflow.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflow.java b/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflow.java
new file mode 100644
index 0000000..8a069d3
--- /dev/null
+++ b/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflow.java
@@ -0,0 +1,195 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness;
+/*
+ *
+ * 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.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+
+import org.apache.taverna.scufl2.api.annotation.Revision;
+import org.apache.taverna.scufl2.api.common.NamedSet;
+import org.apache.taverna.scufl2.api.common.Visitor;
+import org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+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.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+
+
+class DummyWorkflow extends Workflow {
+
+	private TreeSet<DataLink> dataLinks = null;
+
+	private TreeSet<ControlLink> controlLinks = null;
+	private NamedSet<InputWorkflowPort> inputPorts = null;
+	private NamedSet<OutputWorkflowPort> outputPorts = null;
+	private NamedSet<Processor> processors = null;
+	private Revision currentRevision = null;
+	@Override
+	public Revision getCurrentRevision() {
+		return currentRevision;
+	}
+
+	@Override
+	public void setCurrentRevision(Revision currentRevision) {
+		this.currentRevision = currentRevision;
+	}
+
+	private WorkflowBundle dummyParent = new WorkflowBundle();
+	
+	private String name2;
+
+	public DummyWorkflow() {
+		
+	}
+
+	public DummyWorkflow(WorkflowBundle parent) {
+		super.setParent(parent);
+	}
+	
+	@Override
+	public WorkflowBundle getParent() {
+		return dummyParent;
+	}
+
+	@Override
+	public String getName() {
+		return name2;
+	}
+
+	@Override
+	public void setName(String name) {
+		name2 = name;
+	}
+		/**
+	 * @return the inputPorts
+	 */
+	@Override
+	public NamedSet<InputWorkflowPort> getInputPorts() {
+		return inputPorts;
+	}
+
+	/**
+	 * @return the outputPorts
+	 */
+	@Override
+	public NamedSet<OutputWorkflowPort> getOutputPorts() {
+		return outputPorts;
+	}
+
+	/**
+	 * @param inputPorts the inputPorts to set
+	 */
+	public void setInputPorts(NamedSet<InputWorkflowPort> inputPorts) {
+		this.inputPorts = inputPorts;
+	}
+
+	/**
+	 * @param outputPorts the outputPorts to set
+	 */
+	public void setOutputPorts(NamedSet<OutputWorkflowPort> outputPorts) {
+		this.outputPorts = outputPorts;
+	}
+
+	@Override
+	public boolean accept(Visitor visitor) {
+		if (visitor.visitEnter(this)) {
+			List<Iterable<? extends WorkflowBean>> children = new ArrayList<Iterable<? extends WorkflowBean>>();
+			if (getInputPorts() != null) {
+				children.add(getInputPorts());
+			}
+			if (getOutputPorts() != null) {
+				children.add(getOutputPorts());
+			}
+			if (getProcessors() != null) {
+				children.add(getProcessors());
+			}
+			if (getDataLinks() != null) {
+				children.add(getDataLinks());
+			}
+			if (getControlLinks() != null) {
+				children.add(getControlLinks());
+			}
+			outer: for (Iterable<? extends WorkflowBean> it : children) {
+				for (WorkflowBean bean : it) {
+					if (!bean.accept(visitor)) {
+						break outer;
+					}
+				}
+			}
+		}
+		return visitor.visitLeave(this);
+	}
+
+	/**
+	 * @return the dataLinks
+	 */
+	@Override
+	public TreeSet<DataLink> getDataLinks() {
+		return dataLinks;
+	}
+
+	/**
+	 * @param dataLinks the dataLinks to set
+	 */
+	public void setDataLinks(TreeSet<DataLink> dataLinks) {
+		this.dataLinks = dataLinks;
+	}
+
+	/**
+	 * @return the controlLinks
+	 */
+	@Override
+	public TreeSet<ControlLink> getControlLinks() {
+		return controlLinks;
+	}
+
+	/**
+	 * @param controlLinks the controlLinks to set
+	 */
+	public void setControlLinks(TreeSet<ControlLink> controlLinks) {
+		this.controlLinks = controlLinks;
+	}
+
+	/**
+	 * @return the processors
+	 */
+	@Override
+	public NamedSet<Processor> getProcessors() {
+		return processors;
+	}
+
+	/**
+	 * @param processors the processors to set
+	 */
+	public void setProcessors(NamedSet<Processor> processors) {
+		this.processors = processors;
+	}
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/f8af1400/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflowBundle.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflowBundle.java b/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflowBundle.java
new file mode 100644
index 0000000..16d15a8
--- /dev/null
+++ b/taverna-scufl2-api/src/test/java/org/apache/taverna/scufl2/validation/correctness/DummyWorkflowBundle.java
@@ -0,0 +1,133 @@
+/**
+ * 
+ */
+package org.apache.taverna.scufl2.validation.correctness;
+/*
+ *
+ * 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.util.ArrayList;
+import java.util.List;
+
+import org.apache.taverna.scufl2.api.common.NamedSet;
+import org.apache.taverna.scufl2.api.common.Visitor;
+import org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.profiles.Profile;
+
+
+/**
+ * @author alanrw
+ *
+ */
+public class DummyWorkflowBundle extends WorkflowBundle {
+	
+	private NamedSet<Profile> profiles = null;
+	private NamedSet<Workflow> workflows = null;
+	private Workflow mainWorkflow;
+	private Profile mainProfile;
+	
+	@Override
+	public boolean accept(Visitor visitor) {
+		if (visitor.visitEnter(this)) {
+			List<Iterable<? extends WorkflowBean>> children = new ArrayList<Iterable<? extends WorkflowBean>>();
+			if (getWorkflows() != null) {
+				children.add(getWorkflows());
+			}
+			if (getProfiles() != null) {
+				children.add(getProfiles());
+			}
+			outer: for (Iterable<? extends WorkflowBean> it : children) {
+				for (WorkflowBean bean : it) {
+					if (!bean.accept(visitor)) {
+						break outer;
+					}
+				}
+			}
+		}
+		return visitor.visitLeave(this);
+	}
+
+	/**
+	 * @return the profiles
+	 */
+	@Override
+	public NamedSet<Profile> getProfiles() {
+		return profiles;
+	}
+
+	/**
+	 * @param profiles the profiles to set
+	 */
+	public void setProfiles(NamedSet<Profile> profiles) {
+		this.profiles = profiles;
+	}
+
+	/**
+	 * @return the workflows
+	 */
+	@Override
+	public NamedSet<Workflow> getWorkflows() {
+		return workflows;
+	}
+
+	/**
+	 * @param workflows the workflows to set
+	 */
+	public void setWorkflows(NamedSet<Workflow> workflows) {
+		this.workflows = workflows;
+	}
+
+	/**
+	 * @return the mainWorkflow
+	 */
+	@Override
+	public Workflow getMainWorkflow() {
+		return mainWorkflow;
+	}
+
+	/**
+	 * @param mainWorkflow the mainWorkflow to set
+	 */
+	@Override
+	public void setMainWorkflow(Workflow mainWorkflow) {
+		this.mainWorkflow = mainWorkflow;
+	}
+
+	/**
+	 * @return the mainProfile
+	 */
+	@Override
+	public Profile getMainProfile() {
+		return mainProfile;
+	}
+
+	/**
+	 * @param mainProfile the mainProfile to set
+	 */
+	@Override
+	public void setMainProfile(Profile mainProfile) {
+		this.mainProfile = mainProfile;
+	}
+
+
+}