Skip to content

Commit eb349ca

Browse files
author
objectiser
committed
Update to choice projection algorithm and support for 'allowOptional' property. Additional choice projection validation currently causing errors on elements outside scope of what is supposed to be projected, so will need to change where projection is initiated
1 parent a38e4e9 commit eb349ca

14 files changed

Lines changed: 265 additions & 109 deletions

File tree

bundles/org.scribble.protocol.projection/src/main/java/org/scribble/protocol/projection/impl/ChoiceProjectorRule.java

Lines changed: 119 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
package org.scribble.protocol.projection.impl;
1717

1818
import java.text.MessageFormat;
19+
import java.util.Collections;
1920
import java.util.logging.Logger;
2021

2122
import org.scribble.common.logging.Journal;
23+
import org.scribble.protocol.model.Activity;
2224
import org.scribble.protocol.model.Block;
2325
import org.scribble.protocol.model.Choice;
24-
import org.scribble.protocol.model.MessageSignature;
2526
import org.scribble.protocol.model.ModelObject;
2627
import org.scribble.protocol.model.Role;
2728
import org.scribble.protocol.util.InteractionUtil;
@@ -32,6 +33,8 @@
3233
*/
3334
public class ChoiceProjectorRule implements ProjectorRule {
3435

36+
public static final String ALLOW_OPTIONAL = "scribble.choice.allowOptional";
37+
3538
private static final Logger LOG=Logger.getLogger(ChoiceProjectorRule.class.getName());
3639

3740
/**
@@ -59,11 +62,7 @@ public boolean isSupported(ModelObject obj) {
5962
public Object project(ProjectorContext context, ModelObject model,
6063
Role role, Journal l) {
6164
Choice projected=new Choice();
62-
Object ret=projected;
6365
Choice source=(Choice)model;
64-
boolean merge=false;
65-
boolean rolesSet=false;
66-
Role fromRole=null;
6766
boolean optional=false;
6867

6968
projected.derivedFrom(source);
@@ -80,9 +79,7 @@ public Object project(ProjectorContext context, ModelObject model,
8079
l);
8180

8281
if (block != null) {
83-
84-
// TODO: Temporary fix to merge nested choice
85-
if (block.getContents().size() == 1
82+
if (block.getContents().size() == 1
8683
&& block.getContents().get(0) instanceof Choice
8784
&& isSameRole(projected, (Choice)block.getContents().get(0))) {
8885
projected.getPaths().addAll(((Choice)block.getContents().get(0)).getPaths());
@@ -93,82 +90,34 @@ && isSameRole(projected, (Choice)block.getContents().get(0))) {
9390
optional = true;
9491
}
9592
}
96-
97-
// Check if block needs to be merged
98-
if (merge) {
99-
Role destination=null;
100-
101-
// Check if initial interactions have same destination
102-
for (Block block : projected.getPaths()) {
103-
104-
java.util.List<ModelObject> list=
105-
org.scribble.protocol.util.InteractionUtil.getInitialInteractions(block);
106-
for (ModelObject act : list) {
107-
Role r=InteractionUtil.getToRole(act);
108-
109-
if (destination == null) {
110-
destination = r;
111-
} else if (!destination.equals(r)) {
112-
merge = false;
113-
break;
114-
}
115-
}
116-
117-
if (!merge) {
118-
break;
119-
}
120-
}
121-
}
12293

123-
if (merge) {
124-
java.util.List<Block> tmp=new java.util.Vector<Block>(projected.getPaths());
125-
126-
for (Block block : tmp) {
127-
java.util.List<ModelObject> list=
128-
org.scribble.protocol.util.InteractionUtil.getInitialInteractions(block);
129-
130-
// Remove block
131-
projected.getPaths().remove(block);
132-
133-
for (ModelObject act : list) {
134-
MessageSignature ms=InteractionUtil.getMessageSignature(act);
135-
boolean add=true;
136-
137-
for (Block wb : projected.getPaths()) {
138-
MessageSignature wbms=InteractionUtil.getMessageSignature(wb);
139-
140-
if (ms.equals(wbms)) {
141-
// TODO: Need to check paths for conformance.
142-
// If conforms, then merge, if not, then error
143-
l.error("MERGING NOT CURRENTLY SUPPORTED", null);
144-
145-
add = false;
146-
}
147-
}
148-
149-
if (add) {
150-
151-
// Check if roles should be set or matched
152-
if (!rolesSet) {
153-
fromRole = InteractionUtil.getFromRole(act);
154-
//toRole = InteractionUtil.getToRole(act);
155-
156-
rolesSet = true;
157-
} else {
158-
// TODO: Check that roles match
159-
LOG.info("TODO: Check that roles match");
160-
}
161-
}
162-
}
163-
}
164-
}
165-
166-
if (merge) {
167-
projected.setRole(fromRole);
168-
}
94+
return(processChoice(context, projected, role, l, optional));
95+
}
96+
97+
/**
98+
* This method processes a choice construct.
99+
*
100+
* @param context The context
101+
* @param projected The projected choice
102+
* @param role The projected role
103+
* @param l The journal
104+
* @param optional Whether the construct is optional
105+
* @return The processed activities
106+
*/
107+
protected static Object processChoice(ProjectorContext context, Choice projected, Role role,
108+
Journal l, boolean optional) {
109+
Object ret=null;
169110

170111
ret = extractCommonBehaviour(context, projected, role, l);
171112

113+
114+
// If multiple paths have common initiator (but not common to all behaviours)
115+
// then group in sub-choice
116+
groupSubpathsWithCommonInitiator(context, projected, role, l);
117+
118+
// Confirm each path has distinct initial interactions
119+
checkForAmbiguity(context, projected, role, l);
120+
172121
// Remove all empty paths
173122
for (int i=projected.getPaths().size()-1; i >= 0; i--) {
174123
Block b=projected.getPaths().get(i);
@@ -187,12 +136,102 @@ && isSameRole(projected, (Choice)block.getContents().get(0))) {
187136
}
188137
projected = null;
189138
} else if (optional) {
139+
140+
// Check if optional permitted
141+
if (System.getProperty(ALLOW_OPTIONAL, "false").
142+
equalsIgnoreCase(Boolean.FALSE.toString())){
143+
l.error(MessageFormat.format(
144+
java.util.PropertyResourceBundle.getBundle("org.scribble.protocol.projection.Messages").
145+
getString("_CHOICE_EMPTY_PATH"),
146+
role.getName()), projected.getProperties());
147+
}
148+
190149
// Add optional block
191150
projected.getPaths().add(new Block());
192151
}
193152

194153
return (ret);
195154
}
155+
156+
/**
157+
* This method checks whether choice paths should be grouped into sub-paths
158+
* with common initiating interaction sentences.
159+
*
160+
* @param context The context
161+
* @param projected The projected choice
162+
* @param role The role
163+
* @param l The journal
164+
*/
165+
@SuppressWarnings("unchecked")
166+
protected static void groupSubpathsWithCommonInitiator(ProjectorContext context, Choice projected,
167+
Role role, Journal l) {
168+
java.util.Map<Activity, java.util.List<Block>> pathGroups=
169+
new java.util.HashMap<Activity, java.util.List<Block>>();
170+
171+
for (Block path : projected.getPaths()) {
172+
if (path.size() > 0) {
173+
java.util.List<Block> plist=pathGroups.get(path.get(0));
174+
if (plist == null) {
175+
plist = new java.util.Vector<Block>();
176+
pathGroups.put(path.get(0), plist);
177+
}
178+
plist.add(path);
179+
}
180+
}
181+
182+
for (Activity act : pathGroups.keySet()) {
183+
java.util.List<Block> plist=pathGroups.get(act);
184+
185+
if (plist.size() >= 2) {
186+
187+
// Create new choice construct
188+
Choice sub=new Choice();
189+
int pos=-1;
190+
191+
// TODO: Do we need to determine/set the role associated with choice?
192+
// If different then report error?
193+
194+
for (Block b : plist) {
195+
if (pos == -1) {
196+
pos = projected.getPaths().indexOf(b);
197+
}
198+
projected.getPaths().remove(b);
199+
sub.getPaths().add(b);
200+
}
201+
202+
Block newPath=new Block();
203+
204+
projected.getPaths().add(pos, newPath);
205+
206+
Object processed=processChoice(context, sub, role, l, false);
207+
208+
if (processed instanceof java.util.List<?>) {
209+
newPath.getContents().addAll((java.util.List<Activity>)processed);
210+
} else {
211+
LOG.severe("Should have returned a list with extracted common activity(s) followed by choice");
212+
}
213+
}
214+
}
215+
}
216+
217+
protected static void checkForAmbiguity(ProjectorContext context, Choice projected,
218+
Role role, Journal l) {
219+
// Confirm each path has distinct initial interactions
220+
java.util.List<ModelObject> interactions=new java.util.Vector<ModelObject>();
221+
222+
for (Block path : projected.getPaths()) {
223+
java.util.List<ModelObject> initial=InteractionUtil.getInitialInteractions(path);
224+
225+
if (!Collections.disjoint(interactions, initial)) {
226+
l.error(MessageFormat.format(
227+
java.util.PropertyResourceBundle.getBundle("org.scribble.protocol.projection.Messages").
228+
getString("_AMBIGUOUS_CHOICE"),
229+
role.getName()), null);
230+
} else {
231+
interactions.addAll(initial);
232+
}
233+
}
234+
}
196235

197236
/**
198237
* Extract common behaviour.
@@ -240,33 +279,6 @@ protected static Object extractCommonBehaviour(ProjectorContext context, Choice
240279
projected.getPaths().get(i).getContents().remove(0);
241280
}
242281
} else {
243-
// Check if two or more paths have same first element, and
244-
// therefore are invalid
245-
boolean invalid=false;
246-
247-
for (int i=0; !invalid && i < projected.getPaths().size(); i++) {
248-
249-
for (int j=0; !invalid && j < projected.getPaths().size(); j++) {
250-
251-
if (i != j) {
252-
Block b1=projected.getPaths().get(i);
253-
Block b2=projected.getPaths().get(j);
254-
255-
if (b1.size() > 0 && b2.size() > 0
256-
&& b1.get(0).equals(b2.get(0))) {
257-
invalid = true;
258-
}
259-
}
260-
}
261-
}
262-
263-
if (invalid) {
264-
l.error(MessageFormat.format(
265-
java.util.PropertyResourceBundle.getBundle("org.scribble.protocol.projection.Messages").
266-
getString("_AMBIGUOUS_CHOICE"),
267-
role.getName()), null);
268-
}
269-
270282
checkPaths = false;
271283
}
272284

bundles/org.scribble.protocol.projection/src/main/resources/org/scribble/protocol/projection/Messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
# *
1616
# */
1717
_AMBIGUOUS_CHOICE=Choice at role ''{0}'' is ambiguous
18+
_CHOICE_EMPTY_PATH=Choice for role ''{0}'' is not projected to all paths
1819
_NOT_PROJECTED_MODEL=Model ''{0}'' was not projected to another model
1920
_UNKNOWN_ROLE=Role ''{0}'' is not defined within this protocol model

qa/org.scribble.protocol.ctk/src/test/java/org/scribble/protocol/ctk/CTKUtil.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ public static void checkProjectsSuccessfully(String globalModelFile, String loca
356356

357357
Role role=new Role(projectAsRole);
358358
ProtocolModel projected=project(model, role, logger, context);
359+
360+
if (logger.getErrorCount() > 0) {
361+
fail("Projection to role '"+role+"' failed: "+logger.getErrors());
362+
}
359363

360364
verify(projected, expected);
361365
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2009-10 www.scribble.org
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
package org.scribble.protocol.ctk;
18+
19+
import org.junit.Test;
20+
import org.junit.runner.RunWith;
21+
import org.junit.runners.Parameterized;
22+
import org.scribble.protocol.ProtocolContext;
23+
import org.scribble.protocol.projection.impl.ChoiceProjectorRule;
24+
25+
import java.util.LinkedList;
26+
import java.util.List;
27+
28+
@RunWith(Parameterized.class)
29+
public class ProtocolProjectorAllowOptionalTest {
30+
31+
private String globalModelFile;
32+
private String expectedLocalModelFile;
33+
private ProtocolContext context;
34+
35+
public ProtocolProjectorAllowOptionalTest(String globalModelFile, String expectedLocalModelFile, ProtocolContext context) {
36+
this.globalModelFile = globalModelFile;
37+
this.expectedLocalModelFile = expectedLocalModelFile;
38+
this.context = context;
39+
}
40+
41+
@Parameterized.Parameters
42+
public static List<Object[]> testcases() {
43+
Object[][] array = new Object[][]{
44+
{"ChoiceMergeableSimpleAllowOptional.spr", "ChoiceMergeableSimpleAllowOptional@C.spr"}
45+
};
46+
List<Object[]> result = new LinkedList<Object[]>();
47+
for (Object[] sub: array) {
48+
result.add(new Object[] {
49+
"tests/protocol/global/" + sub[0],
50+
"tests/protocol/local/" + sub[1],
51+
(sub.length == 3 ? sub[2] : null)
52+
});
53+
}
54+
return result;
55+
}
56+
57+
@Test
58+
public void doTest() {
59+
System.setProperty(ChoiceProjectorRule.ALLOW_OPTIONAL, "true");
60+
61+
CTKUtil.checkProjectsSuccessfully(globalModelFile, expectedLocalModelFile, context);
62+
63+
System.setProperty(ChoiceProjectorRule.ALLOW_OPTIONAL, "false");
64+
}
65+
}

qa/org.scribble.protocol.ctk/src/test/java/org/scribble/protocol/ctk/ProtocolProjectorRejectsTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public ProtocolProjectorRejectsTest(String globalModelFile, ProtocolContext cont
4747
public static List<Object[]> testcases() {
4848
Object[][] array = new Object[][]{
4949
{"ChoiceNotMergeableSimple.spr", null, "C"},
50+
{"ChoiceNotMergeableSimple2.spr", null, "C"},
5051
//{"ChoiceNotMergeableRecursion.spr", null, "C"}
5152
};
5253
List<Object[]> result = new LinkedList<Object[]>();

qa/org.scribble.protocol.ctk/src/test/java/org/scribble/protocol/ctk/ProtocolProjectorTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public ProtocolProjectorTest(String globalModelFile, String expectedLocalModelFi
4040
@Parameterized.Parameters
4141
public static List<Object[]> testcases() {
4242
Object[][] array = new Object[][]{
43-
43+
{"ChoiceMergeableSimple.spr", "ChoiceMergeableSimple@C.spr"},
44+
4445
{"Annotation.spr", "Annotation@Buyer.spr"}, // 0
4546
{"Annotation.spr", "Annotation@Seller.spr"}, // 1
4647

0 commit comments

Comments
 (0)