Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

End to end flow instantiation creates nonsense flow #2009

Closed
AaronGreenhouse opened this issue Oct 4, 2019 · 21 comments · Fixed by #2081
Closed

End to end flow instantiation creates nonsense flow #2009

AaronGreenhouse opened this issue Oct 4, 2019 · 21 comments · Fixed by #2081

Comments

@AaronGreenhouse
Copy link
Contributor

@AaronGreenhouse AaronGreenhouse commented Oct 4, 2019

In the example below, the end to end flow e2e is broken because the destination of connection c1 doesn't match the starting point of flow fpath. An error about this is generated on the declarative model. But when you instantiate the system top.specific an end to end flow instance for e2e is created, and it contains the erroneous connection. No error is reported on the instance model.

package fred2
public
    thread th
        features
            i: in event port;
            i_wrong: in event port;
            o: out event port;
        flows
            fpath: flow path i -> o;
    end th;
    
    thread implementation th.specific
--        flows
--            fpath: flow path i -> o;
    end th.specific;
        
    thread th2
        features
            o: out event port;
            i: in event port;
        flows
            fsrc: flow source o;
            fsnk: flow sink i;
    end th2;
    
    thread implementation th2.i
        -- trivial
    end th2.i;
    
    process top
    end top;
    
    process implementation top.specific
        subcomponents
            t: thread th.specific;
            q: thread th2.i;
        connections
            c1: port q.o -> t.i_wrong;
            c2: port t.o -> q.i;
        flows
            e2e: end to end flow q.fsrc -> c1 -> t.fpath -> c2 -> q.fsnk;
    end top.specific;
end fred2;

However, if you uncomment the (superfluous) flow path implementation in th.specific, then the end to end flow is not created and the instance model has the error message:

Cannot create end to end flow 'e2e' because the end of the semantic connection 'q.o -> t.i_wrong' does not connect to the start of flow 'fpath'

This is because the error checking of connection endpoints in instantiation only happens when there is a flow implementation. The methods isValidContinuation() are used.

(These methods are currently causing other problems, however, see issue #1984.)

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Nov 26, 2019

I tried the same case with outgoing connections:

    process implementation top.specific2
        subcomponents
            t: thread th.specific;
            q: thread th2.i;
        connections
            c1: port q.o -> t.i;
            c2: port t.o_wrong -> q.i;
        flows
            e2e: end to end flow q.fsrc -> c1 -> t.fpath -> c2 -> q.fsnk;
    end top.specific;

The instance model correctly ignores this because the collectConnectionInstances returns an empty set when looking for the incoming connections to fsnk in processFlowStep.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Nov 26, 2019

I think the fix is to update

				final List<ConnectionInstance> connectionsToUse = new ArrayList<>();
				for (final ConnectionInstance ciToCheck : connis) {
					if ((flowFilter == null || isValidContinuation(etei, flowFilter, ciToCheck))
							&& (nextFlowImpl == null || isValidContinuation(etei, ciToCheck, nextFlowImpl))) {
						connectionsToUse.add(ciToCheck);
					}
				}

so that when nextFlowImpl == null a new version of isValidContinuation() that works with FlowSpecifications is used.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Nov 27, 2019

Okay, nothing is easy. This breaks one of the Serializer2Test tests. The problem comes from connection instances burrowing into subcomponents. In this case, the test I added above doesn't work because the end point of the connection instance is a feature of a subcomponent of the subcomponent, where the endpoint of might be a feature of the outer subcomponent. Specifically, we have problems when the flow named in the end to end flow is not fleshed out as a flow implementation in a component implementation.

Need to change things to start looking the the connections that make up the connection instance.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 2, 2019

Above problem is distilled in the following example. The end to end flow is not instantiated in top.withImpl_implNoFlow, but is in the other two systems.

package test
public
	system s1
		features
			o: out event port;
		flows
			src: flow source o;
	end s1;
	
	system s2
		features
			i: in event port;
			i_wrong: in event port;
		flows
			snk: flow sink i;
	end s2;

	system s3
		features 
			i2: in event port;
			i2_wrong: in event port;
		flows
			snk2: flow sink i2;
	end s3;

	system implementation s2.implFlow
		subcomponents
			s3: system s3;
		connections
			c2: port i -> s3.i2;
			c2_wrong: port i_wrong -> s3.i2_wrong;
		flows
			snk: flow sink i -> c2 -> s3.snk2;
	end s2.implFlow;

	system implementation s2.implNoFlow
		subcomponents
			s3: system s3;
		connections
			c2: port i -> s3.i2;
			c2_wrong: port i_wrong -> s3.i2_wrong;
	end s2.implNoFlow;

	system top
	end top;
	
	system implementation top.typeOnly
		subcomponents
			s1: system s1;
			s2: system s2;
		connections
			c1: port s1.o -> s2.i;
			c1_wrong: port s1.o -> s2.i_wrong;
		flows
			etef1: end to end flow s1.src -> c1 -> s2.snk; -- should be instantiated
			etef1_wrong: end to end flow s1.src -> c1_wrong -> s2.snk; -- should never be instantiated
	end top.typeOnly;
	
	system implementation top.withImpl_implFlow
		subcomponents
			s1: system s1;
			s2: system s2.implFlow;
		connections
			c1: port s1.o -> s2.i;
			c1_wrong: port s1.o -> s2.i_wrong;
		flows
			etef1: end to end flow s1.src -> c1 -> s2.snk; -- should be instantiated
			etef1_wrong: end to end flow s1.src -> c1_wrong -> s2.snk; -- should never be instantiated
	end top.withImpl_implFlow;
	
	system implementation top.withImpl_implNoFlow
		subcomponents
			s1: system s1;
			s2: system s2.implNoFlow;
		connections
			c1: port s1.o -> s2.i;
			c1_wrong: port s1.o -> s2.i_wrong;
		flows
			etef1: end to end flow s1.src -> c1 -> s2.snk; -- should be instantiated, but currently is not
			etef1_wrong: end to end flow s1.src -> c1_wrong -> s2.snk; -- should never be instantiated
	end top.withImpl_implNoFlow;
end test;

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 2, 2019

New problems

Failures:
Issue1830Test.testForEndToEndFlow:62 expected:<0> but was:<1>
Issue567Test.issue567:69 In S_top_instance: Expected 1 end to end flow but found 0
Issue575Test.issue575:69 In S_top_instance: Expected 1 end to end flow but found 0

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 3, 2019

Need to check for refinement along the features.

Fixed that, which fixed 1830, but 567 and 575 are still broken. I haven't looked at them yet.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 3, 2019

Problem with 567 and 575 is feature groups. Need to handle those differently.

@lwrage lwrage added this to the 2.6.1 milestone Dec 4, 2019
@lwrage lwrage removed this from the 2.6.1 milestone Dec 4, 2019
@lwrage lwrage added this to the 2.7.0 milestone Dec 4, 2019
@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 6, 2019

Updated isValidContinuation() to deal with feature groups:

	boolean isValidContinuation(EndToEndFlowInstance etei, ConnectionInstance conni, FlowSpecification fspec) {
		/*
		 * Issue 2009: Check if the connection instance connects to the flow spec. Here we have a weird
		 * situation. If the subcomponent the flow spec is qualified by is only described by a component type, then
		 * connection end is going to match up with the beginning of the flow. That's fine. If the component
		 * has a classifier implementation AND the flow spec has a flow implementation, then everything will also
		 * fine: either the connection instance is correct and reaches the start of the flow implementation, or it
		 * is incorrect and doesn't. But we can also have the case that the subcomponent is described by a
		 * component implementation but the flow spec does not have a flow implementation. In this case we
		 * still may have the case the connection instance continues into the subcomponent and terminates at a
		 * subsubcomponent. But the flow spec that we have here will be at the edge of the original subcomponent.
		 * so the end connection instance will not match up with the start of the flow spec.
		 *
		 * So what we really need to do is walk backwards along the connections that make up the connection instance
		 * until we find one that connects to the flow because as wee the connection instance may "punch through" the
		 * subcomponent.
		 */
		final Context flowCxt = fspec.getInEnd().getContext();
		final Feature flowIn = fspec.getInEnd().getFeature();
		final List<Feature> flowInRefined = flowIn.getAllFeatureRefinements();
		final EList<ConnectionReference> connRefs = conni.getConnectionReferences();
		int idx = connRefs.size() - 1;
		boolean result = false;
		while (!result && idx >= 0) {
			final Connection conn = connRefs.get(idx).getConnection();
			final ConnectionEnd connEnd = conn.getDestination().getConnectionEnd();
			if (connEnd instanceof Feature) {
				final List<Feature> connEndRefined = ((Feature) connEnd).getAllFeatureRefinements();
				if (flowCxt instanceof FeatureGroup) {
					result = connEndRefined.contains(flowCxt);
				} else {
					result = flowInRefined.contains(connEnd) || connEndRefined.contains(flowIn);
				}
			}
			idx -= 1;
		}
		return result;
	}

This handles the cases where the connection ends at fg and the flow starts at fg or fg.i. It is not possible to have the case where the connection ends at fg.i and the flow starts at fg.i. This is because the actual case were are concerned about the code that gets us here is a connection between to peer components. In this case, the connection must be between to feature groups directly. Feature groups can only be taken apart or put together inside a component, either going down from a parent to child or up from a child to parent, respectively.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 6, 2019

Need to add the unit tests for this case.

I worry still that flows along access connections may not work correctly.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 6, 2019

Pushed out changes and unit test.

Access connections should not be a problem because they are only interesting at the very end where they connect to a subcomponent. This situation isn't applicable here, because as said above, this problem concerns the the boundary of a component.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 6, 2019

"All checks pass" on the pull request now.

@lwrage
Copy link
Contributor

@lwrage lwrage commented Dec 6, 2019

Your comment above "It is not possible to have the case where the connection ends at fg.i and the flow starts at fg.i." is not correct. If the connection between two subcomponents is a feature connection you can reach into the feature group at the connection ends.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 6, 2019

That's what I initially thought, but I cannot get that to work, I get syntax errors:

package xxx
public
	feature group fg
		features
			i: in event port;
			o: out event port;
	end fg;

	feature group fg2
		features
			o: out event port;
			i: in event port;
		inverse of fg
	end fg2;
	
	system u
		features
			i: in event port;
			o: out event port;
			fg: feature group fg;
	end u;
	
	system u2
		features
			i: in event port;
			o: out event port;
			fg2: feature group fg2;
	end u;
	
	system t
		features
			i: in event port;
			o: out event port;
			fg: feature group fg;
	end t;

	-- test parent to child feature group connections
	system implementation t.i
		subcomponents
			u: system u;
		connections
			c1: feature group fg <-> u.fg;
			c2: port fg.i -> u.i;
			c3: port u.o -> fg.o;
			
--			c4: port i -> u.fg.i;  -- illegal
--			c5: port u.fg.o -> o;  -- illegal
	end t.i;
	

	-- test peer to peer feature group connections
	system implementation t.j
		subcomponents
			u1: system u;
			u2: system u2;
		connections
			c1: feature group u1.fg <-> u2.fg2;
			c2: feature group u2.fg2 <-> u1.fg;
			
			c3: feature group u1.fg.o -> u2.fg.i; -- illegal
	end t.i;	
end xxx;

I get syntax errors on c3 in t.j. Also doesn't work if I make that a port connection.

@lwrage
Copy link
Contributor

@lwrage lwrage commented Dec 6, 2019

That is a feature group connection. It should work when you remove the keyword group.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 6, 2019

got it

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 6, 2019

Okay, new set of tests for "fg.i" to "fg.i" via feature connections:

package test_fg2
public
	feature group fg1
		features
			o: out event port;
			o_wrong: out event port;
	end fg1;
	
	feature group fg2
		features
			i: in event port;
			i_wrong: in event port;
		inverse of fg1
	end fg2;
	
	system s1
		features
			fg: feature group fg1;
		flows
			srcF: flow source fg.o;
			src: flow source fg;
	end s1;
	
	system s2
		features
			fg: feature group fg2;
			fg_wrong: feature group fg2;
		flows
			-- test matching of "fg.i" on connection with "fg.i" on the flow
			snkF: flow sink fg.i;
			
			-- test matching of "fg.i" on connection with "fg" on the flow
			snk: flow sink fg;
	end s2;

	system s3
		features 
			fg: feature group fg2;
			fg_wrong: feature group fg2;
		flows
			-- test matching of "fg.i on connection with "fg.i" on the flow
			snk2F: flow sink fg.i;
			
			-- test matching of "fg" on connection with "fg" on the flow
			snk2: flow sink fg;
	end s3;

	system implementation s2.implFlow
		subcomponents
			s3: system s3;
		connections
			c2: feature group fg -> s3.fg;
			c2_wrong: feature group fg_wrong -> s3.fg_wrong;
		flows
			-- test matching of "fg.i" on connection with "fg.i" on the flow
			snkF: flow sink fg.i -> c2 -> s3.snk2F;
			
			-- test matching of "fg" on connection with "fg" on the flow
			snk: flow sink fg -> c2 -> s3.snk2;
	end s2.implFlow;

	system implementation s2.implNoFlow
		subcomponents
			s3: system s3;
		connections
			c2: feature group fg -> s3.fg;
			c2_wrong: feature group fg_wrong -> s3.fg_wrong;
	end s2.implNoFlow;

	system top
	end top;
	
	system implementation top.typeOnly
		subcomponents
			s1: system s1;
			s2: system s2;
		connections
			-- correct
			c1: feature s1.fg.o -> s2.fg.i;
			-- correct feature group, wrong subfeature
			c1_wrong1: feature s1.fg.o -> s2.fg.i_wrong;
			-- incorrect feature group
			c1_wrong2: feature s1.fg.o -> s2.fg_wrong.i;
		flows
			etef1F: end to end flow s1.srcF -> c1 -> s2.snkF; -- should be instantiated
			etef1: end to end flow s1.src -> c1 -> s2.snk; -- should be instantiated
			
			etef1F_wrong1: end to end flow s1.srcF -> c1_wrong1 -> s2.snkF; -- should NOT be instantiated
			etef1_wrong1: end to end flow s1.src -> c1_wrong1 -> s2.snk; -- should NOT be instantiated???
			
			etef1F_wrong2: end to end flow s1.srcF -> c1_wrong2 -> s2.snkF; -- should NOT be instantiated
			etef1_wrong2: end to end flow s1.src -> c1_wrong2 -> s2.snk; -- should NOT be instantiated
	end top.typeOnly;
	
	
	
	
	
	
	system implementation top.withImpl_implFlow
		subcomponents
			s1: system s1;
			s2: system s2.implFlow;
		connections
			-- correct
			c1: feature s1.fg.o -> s2.fg.i;
			-- correct feature group, wrong subfeature
			c1_wrong1: feature s1.fg.o -> s2.fg.i_wrong;
			-- incorrect feature group
			c1_wrong2: feature s1.fg.o -> s2.fg_wrong.i;
		flows
			etef1F: end to end flow s1.srcF -> c1 -> s2.snkF; -- should be instantiated
			etef1: end to end flow s1.src -> c1 -> s2.snk; -- should be instantiated
			
			etef1F_wrong1: end to end flow s1.srcF -> c1_wrong1 -> s2.snkF; -- should NOT be instantiated
			etef1_wrong1: end to end flow s1.src -> c1_wrong1 -> s2.snk; -- should NOT be instantiated???
			
			etef1F_wrong2: end to end flow s1.srcF -> c1_wrong2 -> s2.snkF; -- should NOT be instantiated
			etef1_wrong2: end to end flow s1.src -> c1_wrong2 -> s2.snk; -- should NOT be instantiated
	end top.withImpl_implFlow;
	
	system implementation top.withImpl_implNoFlow
		subcomponents
			s1: system s1;
			s2: system s2.implNoFlow;
		connections
			-- correct
			c1: feature s1.fg.o -> s2.fg.i;
			-- correct feature group, wrong subfeature
			c1_wrong1: feature s1.fg.o -> s2.fg.i_wrong;
			-- incorrect feature group
			c1_wrong2: feature s1.fg.o -> s2.fg_wrong.i;
		flows
			etef1F: end to end flow s1.srcF -> c1 -> s2.snkF; -- should be instantiated
			etef1: end to end flow s1.src -> c1 -> s2.snk; -- should be instantiated
			
			etef1F_wrong1: end to end flow s1.srcF -> c1_wrong1 -> s2.snkF; -- should NOT be instantiated
			etef1_wrong1: end to end flow s1.src -> c1_wrong1 -> s2.snk; -- should NOT be instantiated???
			
			etef1F_wrong2: end to end flow s1.srcF -> c1_wrong2 -> s2.snkF; -- should NOT be instantiated
			etef1_wrong2: end to end flow s1.src -> c1_wrong2 -> s2.snk; -- should NOT be instantiated
	end top.withImpl_implNoFlow;
end test_fg2;

This doesn't work correctly. The etef1F_wrong2 and etef1_wrong2 end to end flows are (correctly) not created, but the end to end to end flow etef1F_wrong1 is created. Flow etef1_wrong1 is created, and believe that is correct.

I need to update isValidContinuation to deal with the case where the connection destination is a ConnectedElement with a non-null next field.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 9, 2019

Updated isValidContinuation (for flow specifications) to deal with feature connection ends and handle the above examples.

	boolean isValidContinuation(EndToEndFlowInstance etei, ConnectionInstance conni, FlowSpecification fspec) {
		/*
		 * Issue 2009: Check if the connection instance connects to the flow spec. Here we have a weird
		 * situation. If the subcomponent the flow spec is qualified by is only described by a component type, then
		 * connection end is going to match up with the beginning of the flow. That's fine. If the component
		 * has a classifier implementation AND the flow spec has a flow implementation, then everything will also
		 * fine: either the connection instance is correct and reaches the start of the flow implementation, or it
		 * is incorrect and doesn't. But we can also have the case that the subcomponent is described by a
		 * component implementation but the flow spec does not have a flow implementation. In this case we
		 * still may have the case the connection instance continues into the subcomponent and terminates at a
		 * subsubcomponent. But the flow spec that we have here will be at the edge of the original subcomponent.
		 * so the end connection instance will not match up with the start of the flow spec.
		 *
		 * So what we really need to do is walk backwards along the connections that make up the connection instance
		 * until we find one that connects to the flow because as wee the connection instance may "punch through" the
		 * subcomponent.
		 */
		final Context flowCxt = fspec.getInEnd().getContext();
		final Feature flowIn = fspec.getInEnd().getFeature();
		final List<Feature> flowInRefined = flowIn.getAllFeatureRefinements();
		final EList<ConnectionReference> connRefs = conni.getConnectionReferences();
		int idx = connRefs.size() - 1;
		boolean result = false;
		while (!result && idx >= 0) {
			final Connection conn = connRefs.get(idx).getConnection();
			final ConnectedElement connDestination = conn.getDestination();
			final ConnectionEnd connEnd = connDestination.getConnectionEnd();
			if (connEnd instanceof Feature) {
				final List<Feature> connEndRefined = ((Feature) connEnd).getAllFeatureRefinements();
				if (flowCxt instanceof FeatureGroup) {
					result = connEndRefined.contains(flowCxt);
					if (result && connDestination.getNext() != null) { // connection is a feature group connection and might me "fg.f"
						final ConnectionEnd connEnd2 = connDestination.getNext().getConnectionEnd();
						if (connEnd2 instanceof Feature) {
							final List<Feature> connEndRefined2 = ((Feature) connEnd2).getAllFeatureRefinements();
							result = flowInRefined.contains(connEnd2) || connEndRefined2.contains(flowIn);
						} else {
							result = false;
						}
					}
				} else {
					result = flowInRefined.contains(connEnd) || connEndRefined.contains(flowIn);
				}
			}
			idx -= 1;
		}
		return result;
	}

This seems to work. The top.typeOnly and top.withImpl_implNoFlow cases work. For these, the end to end flows etef1F, etef1, and etef1_wrong1 are instantiated. The flow etef1_wrong1 should be instantiated (and so this is correct) because the flow sink sink doesn't specific a sub feature of the feature group, so it doesn't matter that the connection uses i_wrong instead of I.

The case top.withImpl_implFlow doesn't work. It has the three above end to end flow instances, but also incorrectly the flow instance for etef1F_wrong1. I believe this is because the original code that handles flow implementations (we have added dealing with flow specifications so far) doesn't handle the case of feature connections.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 9, 2019

Upon further review and much debugging, I think that etef1F_wrong1 appearing in top.withImpl_implFlow is okay. Processing of the flow in this case is different from the other two top level systems because the flow specs have associated flow implementations. At no point during this processing are the end points of the flows checked against the end points of the connections. But that is okay, because the end to end flow that is created references a connection instance that makes sense, one built from c1_wrong1 and c2. This is sensible because c2 is a plain feature group connection so it doesn't matter which sub feature is named in the destination of c1_wrong.

We need to remember what the original point of this issue was: instantiating a declarative model with errors was generating an instance model that made no sense. The end to end flows that were generated were nonsense because the connections didn't connect to the flows. I believe I have eliminated the possibility of producing nonsense flows. It is still possible that flows may be generated for end to end flows that are marked with error in the declarative model. But they won't be nonsensical.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 10, 2019

Tests for 567 and 575 still fail.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 10, 2019

Problem is bidirectional connections. Should be easy to fix.

@AaronGreenhouse
Copy link
Contributor Author

@AaronGreenhouse AaronGreenhouse commented Dec 10, 2019

Fixed isValidContinuation() on last time:

	boolean isValidContinuation(EndToEndFlowInstance etei, ConnectionInstance conni, FlowSpecification fspec) {
		/*
		 * Issue 2009: Check if the connection instance connects to the flow spec. Here we have a weird
		 * situation. If the subcomponent the flow spec is qualified by is only described by a component type, then
		 * connection end is going to match up with the beginning of the flow. That's fine. If the component
		 * has a classifier implementation AND the flow spec has a flow implementation, then everything will also
		 * fine: either the connection instance is correct and reaches the start of the flow implementation, or it
		 * is incorrect and doesn't. But we can also have the case that the subcomponent is described by a
		 * component implementation but the flow spec does not have a flow implementation. In this case we
		 * still may have the case the connection instance continues into the subcomponent and terminates at a
		 * subsubcomponent. But the flow spec that we have here will be at the edge of the original subcomponent.
		 * so the end connection instance will not match up with the start of the flow spec.
		 *
		 * So what we really need to do is walk backwards along the connections that make up the connection instance
		 * until we find one that connects to the flow because as wee the connection instance may "punch through" the
		 * subcomponent.
		 */
		final Context flowCxt = fspec.getInEnd().getContext();
		final Feature flowIn = fspec.getInEnd().getFeature();
		final List<Feature> flowInRefined = flowIn.getAllFeatureRefinements();
		final EList<ConnectionReference> connRefs = conni.getConnectionReferences();
		int idx = connRefs.size() - 1;
		boolean result = false;
		while (!result && idx >= 0) {
			final Connection conn = connRefs.get(idx).getConnection();
			result = isValidContinuationConnectionEnd(flowCxt, flowIn, flowInRefined, conn.getDestination());
			if (!result && conn.isBidirectional()) {
				result = isValidContinuationConnectionEnd(flowCxt, flowIn, flowInRefined, conn.getSource());
			}
			idx -= 1;
		}
		return result;
	}

	private boolean isValidContinuationConnectionEnd(final Context flowCxt, final Feature flowIn,
			final List<Feature> flowInRefined, final ConnectedElement connElement) {
		boolean result = false;
		final ConnectionEnd connEnd = connElement.getConnectionEnd();
		if (connEnd instanceof Feature) {
			final List<Feature> connEndRefined = ((Feature) connEnd).getAllFeatureRefinements();
			if (flowCxt instanceof FeatureGroup) { // src end of the flow is "fg.f"
				result = connEndRefined.contains(flowCxt);
				// if connDestination.getNext() is null then dest end of connection is "fg"
				if (result && connElement.getNext() != null) {
					final ConnectionEnd connEnd2 = connElement.getNext().getConnectionEnd();
					if (connEnd2 instanceof Feature) {
						// check "fg.f" to "fg.f"
						final List<Feature> connEndRefined2 = ((Feature) connEnd2).getAllFeatureRefinements();
						result = flowInRefined.contains(connEnd2) || connEndRefined2.contains(flowIn);
					} else {
						// Connection doesn't end in feature, so no match
						result = false;
					}
				}
			} else {
				// checks "f" to "f" or "fg" to "fg"
				result = flowInRefined.contains(connEnd) || connEndRefined.contains(flowIn);
			}
		}
		return result;
	}

@lwrage lwrage added the core label Jan 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants