/*
* Copyright 2005-8 Pi4 Technologies Ltd
* Copyright 2012 Red Hat, Inc.
*
* Licensed 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.
*
*
* Change History:
* 13 Dec 2008 : Initial version created by gary
* Feb 2012 : Update based on scribble v2
*/
package org.savara.protocol.model.stateless;
import org.savara.protocol.model.util.ChoiceUtil;
import org.savara.protocol.model.util.RepeatUtil;
import org.scribble.common.logging.Journal;
import org.scribble.protocol.model.*;
import org.scribble.protocol.util.InteractionUtil;
/**
* This class implements the stateless transformation
* rule for the Block component.
*/
public class BlockStatelessTransformationRule
extends AbstractStatelessTransformationRule {
/**
* This method determines whether the stateless
* transformation rule is applicable to the
* supplied model object.
*
* @param modelObject The model object
* @return Whether the model object can be transformed
*/
public boolean isSupported(ModelObject modelObject) {
return(modelObject instanceof Block);
}
/**
* This method transforms the supplied model object into
* a stateless equivalent.
*
* @param context The context
* @param modelObject The model object to transform
* @param journal The journal
* @return The transformed object
*/
public ModelObject transform(StatelessTransformationContext context,
ModelObject modelObject, Journal journal) {
return(null);
}
/**
* This method transforms the supplied block activities into
* a stateless equivalent.
*
* @param context The context
* @param src The source block
* @param target The target block
* @return Whether the block has been fully transformed
*/
public boolean transform(StatelessTransformationContext context,
Block src, Block target) {
return(processBlock(context, src, target, 0, true));
}
/**
* This method processes the block of activities, to determine
* whether some or all should be in a particular stateless
* path.
*
* @param context The context
* @param block The target block associated with the stateless path
* @param src The source block
* @param pos The position to start in the source block
* @return Whether the complete src block was processed
*/
public static boolean processBlock(StatelessTransformationContext context,
Block src, Block target, int pos, boolean full) {
boolean ret=true;
Block initialPath=context.getCurrentPath();
for (int i=pos; src != null &&
i < src.getContents().size(); i++) {
Activity act=src.getContents().get(i);
if (isWaitState(context, act)) {
if (isMultiPathBehaviour(act)) {
java.util.List<Block> mpbs=getMultiPaths(act);
for (int j=0; j < mpbs.size(); j++) {
// Save return position
context.push(new TransformState(act, src, i));
Block p=context.createNewPath();
if (p != null) {
context.transform(mpbs.get(j),
p);
}
context.pop();
}
if (isConditional(context, act) == false) {
src = null;
}
} else if (isSinglePathBehaviour(act)) {
Block spb=getSinglePath(act);
context.push(new TransformState(act, src, i));
Block p=context.createNewPath();
if (p != null) {
context.transform(spb, p);
}
context.pop();
if (isConditional(context, act) == false) {
src = null;
}
} else {
target = context.createNewPath();
if (target != null) {
ModelObject newact=context.transform(act);
if (newact instanceof Block) {
target.getContents().addAll(((Block)newact).getContents());
} else if (newact instanceof Activity) {
target.getContents().add((Activity)newact);
}
} else {
src = null;
}
}
ret = false;
} else {
if (isSubScope(act)) {
context.push(new TransformState(act, src, i));
}
Block localPath=context.getCurrentPath();
ModelObject newact=context.transform(act);
if (newact instanceof Block) {
target.getContents().addAll(((Block)newact).getContents());
} else if (newact instanceof Activity) {
target.getContents().add((Activity)newact);
}
if (localPath != context.getCurrentPath()) {
ret = false;
}
if (isSubScope(act)) {
context.pop();
// If transformation has changed the return type, then assume
// has wait states, otherwise continue
if (localPath != context.getCurrentPath() ||
!(act instanceof Repeat &&
RepeatUtil.isDecisionMaker((Repeat)act))) {
src = null;
}
}
}
}
if (src != null && full) {
// Came to end of block, so check if state from
// parent construct has been stored to enable
// further processing
//java.util.List<TransformState> stack=
// context.getStack();
boolean f_continue=true;
java.util.List<TransformState> tmpstack=
new java.util.Vector<TransformState>();
TransformState bp=null;
//for (int i=0; f_continue && i < stack.size(); i++) {
//TransformState bp=stack.get(i);
// Only process if the path has changed
//if (initialPath != context.getCurrentPath()) {
while (f_continue && (bp=context.pop()) != null) {
tmpstack.add(bp);
if (bp.getParent() instanceof Repeat) {// &&
//RepeatUtil.isDecisionMaker((Repeat)bp.getParent())) {
// Only process if the path has changed
if (initialPath != context.getCurrentPath()) {
context.disallowNewPaths();
context.push(bp);
ModelObject newact=context.transform(bp.getParent());
context.pop();
if (newact instanceof Block) {
target.getContents().addAll(((Block)newact).getContents());
} else if (newact instanceof Activity) {
target.getContents().add((Activity)newact);
}
context.allowNewPaths();
}
f_continue = false;
} else {
f_continue = processBlock(context, bp.getBlock(),
target, bp.getPosition()+1, false);
}
}
//}
for (int i=tmpstack.size()-1; i >= 0; i--) {
context.push(tmpstack.get(i));
}
}
return(ret);
}
protected static boolean isSubScope(Activity act) {
boolean ret=false;
if (act instanceof Choice ||
act instanceof Parallel ||
act instanceof Repeat ||
act instanceof RecBlock ||
act instanceof Run) {
ret = true;
}
return(ret);
}
protected static boolean isSinglePathBehaviour(Activity act) {
return(act instanceof Repeat);
}
protected static Block getSinglePath(Activity act) {
Block ret=null;
if (act instanceof Repeat) {
ret = ((Repeat)act).getBlock();
}
return(ret);
}
protected static boolean isMultiPathBehaviour(Activity act) {
return(act instanceof Choice || act instanceof Parallel);
}
protected static java.util.List<Block> getMultiPaths(Activity act) {
java.util.List<Block> ret=null;
if (act instanceof Choice) {
ret = ((Choice)act).getPaths();
} else if (act instanceof Parallel) {
ret = ((Parallel)act).getPaths();
}
return(ret);
}
protected static boolean isConditional(StatelessTransformationContext context,
Activity act) {
boolean ret=false;
if (act instanceof Repeat) {
ret = true;
}
return(ret);
}
protected static boolean isWaitState(StatelessTransformationContext context,
Activity act) {
boolean ret=false;
if (act instanceof Choice) {
ret = !ChoiceUtil.isDecisionMaker((Choice)act);
// If RPC based, then check if interactions in each path are responses
if (ret && !context.isMessageBased()) {
Choice choice=(Choice)act;
ret = false;
for (Block path : choice.getPaths()) {
java.util.List<ModelObject> interactions=
InteractionUtil.getInitialInteractions(path);
for (ModelObject mobj : interactions) {
if (!org.savara.protocol.model.util.InteractionUtil.isResponse((Interaction)mobj)) {
ret = true;
break;
}
}
if (ret) {
break;
}
}
}
} else if (act instanceof Repeat) {
ret = !RepeatUtil.isDecisionMaker((Repeat)act);
} else if (act instanceof Interaction) {
ret = !org.savara.protocol.model.util.InteractionUtil.isSend((Interaction)act) &&
(context.isMessageBased() ||
org.savara.protocol.model.util.InteractionUtil.isRequest((Interaction)act));
}
return(ret);
}
}