View Javadoc

1   /*
2    * Copyright 2010 Fraunhofer Gesellschaft, Munich, Germany,
3    * for its Fraunhofer Institute for Computer Architecture and Software
4    * Technology (FIRST), Berlin, Germany. All rights reserved.
5    * http://www.first.fraunhofer.de/
6    */
7   
8   package net.kwfgrid.gwes;
9   
10  import net.kwfgrid.gworkflowdl.structure.*;
11  import net.kwfgrid.gwes.workflowanalyzer.DoubleDistribution;
12  import net.kwfgrid.gwes.util.StringUtils;
13  
14  import java.util.ArrayList;
15  import java.util.List;
16  
17  import org.apache.log4j.Logger;
18  import org.jdom.Element;
19  import org.jdom.Namespace;
20  import org.jdom.IllegalNameException;
21  import org.jdom.Document;
22  import org.jaxen.SimpleNamespaceContext;
23  
24  /**
25   * @author Andreas Hoheisel
26   *         (<a href="http://www.andreas-hoheisel.de">www.andreas-hoheisel.de</a>)
27   * @version $Id: TransitionOccurrence.java 1490 2011-02-18 13:20:36Z hoheisel $
28   */
29  public class TransitionOccurrence {
30  
31      /**
32       * log4j logger.
33       */
34      private final static Logger logger = Logger.getLogger(TransitionOccurrence.class);
35      
36      public final Transition transition;
37  
38      private Activity activity;
39  
40      public TokenParameterList tokens;
41  
42      /**
43       * Properties gathered from all incoming tokens to be copied to outgoing tokens.
44       */
45      private final GenericProperties props;
46  
47      /**
48       * XML context that contains the elements of all incoming tokens, renamed by its edge expression.
49       */
50      private final Document context;
51  
52      /**
53       * Namespace context
54       */
55      private final SimpleNamespaceContext nsContext;
56  
57      public boolean hasXPathEdgeExpressions;
58      public final boolean simulation;
59  
60      /**
61       * The ID of the transition is created using the ids of the incoming tokens.
62       */
63      private final String ID;
64  
65      /**
66       * ID containing also outgoing tokens. use updateID() to update this ID.
67       */
68      private String IDwithOutputs;
69  
70      private DoubleDistribution probabilityDistribution;
71  
72  
73      /**
74       * Constructor that uses next unlocked tokens on input/read/write places to build transition occurrence.
75       * @param transition
76       */
77      public TransitionOccurrence(Transition transition) throws WorkflowFormatException {
78          this.transition = transition;
79          this.activity = null;
80          this.tokens = new TokenParameterList();
81          hasXPathEdgeExpressions = false;
82          simulation = false;
83          props = Factory.newProperties();
84          context = new Document(new Element("data",JdomString.wfSpace));
85          nsContext = new DefaultNamespaceContext();
86  
87          // data group
88          // check transition property "ignore.data.groups"
89          int ignoreDataGroups = TokenUtils.getIgnoreDataGroupsCode(transition);
90          // check transition property "combine.data.groups" (e.g., "a x b")
91          String dataGroup = null;
92          ArrayList<String> dataGroups = null;
93          String[] combineDataGroupsEdgeExpressions = TokenUtils.getCombineDataGroupsEdgeExpressions(transition);
94  
95          if (combineDataGroupsEdgeExpressions != null) dataGroups = new ArrayList<String>();
96  
97          // update namespace context
98          updateNamespaceContextFromTransitionProperties();
99  
100         // identifier
101         StringBuffer idBuffer = new StringBuffer(transition.getID());
102 
103         Edge[] edges;
104         // read tokens
105         edges = transition.getReadEdges();
106         for (Edge edge : edges) {
107             Token token = TokenUtils.getNextUnlockedToken(edge,true,dataGroup,ignoreDataGroups);
108             if (token == null) {
109                 throw new WorkflowFormatException("No suitable token available on readPlace \"" + edge.getPlaceID() + "\" for data.group \"" + dataGroup + "\".");
110             } else {
111                 // put data tokens with edge expression to context
112                 addToContext(token, edge);
113                 // copy properties
114                 updatePropertiesFromToken(token);
115                 // create TokenParameter and add it to list
116                 TokenParameter tp = new TokenParameter(edge, token, TokenParameter.Scope.READ);
117                 tokens.add(tp);
118                 // set data Group
119                 dataGroup = TokenUtils.updateDataGroup(tp, ignoreDataGroups, combineDataGroupsEdgeExpressions, dataGroups);
120                 // update identifier
121                 idBuffer.append(",r:").append(token.getID());
122             }
123         }
124 
125         // input tokens
126         edges = transition.getInEdges();
127         for (Edge edge : edges) {
128             Token token = TokenUtils.getNextUnlockedToken(edge,false,dataGroup,ignoreDataGroups);
129             if (token == null) {
130                 throw new WorkflowFormatException("No suitable token available on inputPlace \"" + edge.getPlaceID() + "\" for data.group \"" + dataGroup + "\".");
131             } else {
132                 // put data tokens with edge expression to context
133                 addToContext(token, edge);
134                 // copy properties
135                 updatePropertiesFromToken(token);
136                 // create TokenParameter an add it to list
137                 TokenParameter tp = new TokenParameter(edge, token, TokenParameter.Scope.INPUT);
138                 tokens.add(tp);
139                 // set data Group
140                 dataGroup = TokenUtils.updateDataGroup(tp, ignoreDataGroups, combineDataGroupsEdgeExpressions, dataGroups);
141                 // update identifier
142                 idBuffer.append(",i:").append(token.getID());
143             }
144         }
145 
146         // write tokens
147         edges = transition.getWriteEdges();
148         for (Edge edge : edges) {
149             Token token = TokenUtils.getNextUnlockedToken(edge,true,dataGroup,ignoreDataGroups);
150             if (token == null) {
151                 throw new WorkflowFormatException("There is no unlocked Token on Place '"+edge.getPlaceID()+"' available!");
152             } else {
153                 if (!hasXPathEdgeExpressions) {
154                     String edgeExpression = edge.getExpression();
155                     if (edgeExpression != null && edgeExpression.indexOf('$')!=-1) hasXPathEdgeExpressions = true;
156                 }
157                 // put data tokens with edge expression to context
158                 addToContext(token, edge);
159                 // copy properties
160                 updatePropertiesFromToken(token);
161                 // create TokenParameter an add it to list
162                 TokenParameter tp = new TokenParameter(edge, token, TokenParameter.Scope.WRITE);
163                 tokens.add(tp);
164                 // set data Group
165                 dataGroup = TokenUtils.updateDataGroup(tp, ignoreDataGroups, combineDataGroupsEdgeExpressions, dataGroups);
166                 // update identifier
167                 idBuffer.append(",w:").append(token.getID());
168             }
169         }
170 
171         // dummy output tokens (real output tokens will be generated by activities or XPath edge expressions)
172         edges = transition.getOutEdges();
173         for (Edge edge : edges) {
174             if (!hasXPathEdgeExpressions) {
175                 String edgeExpression = edge.getExpression();
176                 if (edgeExpression != null && edgeExpression.indexOf('$')!=-1) hasXPathEdgeExpressions = true;
177             }
178             tokens.add(new TokenParameter(edge, TokenParameter.Scope.OUTPUT));
179         }
180 
181         // generate combined data groups property
182         updateDataGroupProperty(dataGroup, dataGroups);
183 
184         // update resource allocation group property
185         updateResourceAllocationGroupProperty();
186 
187         // set id
188         ID = idBuffer.toString();
189         updateID();
190 
191         // logging
192         if(logger.isDebugEnabled()) {
193             logger.debug("constructed "+this.toString());
194         }
195     }
196 
197     /**
198      * Locks the input and - if available - the output tokens.
199      */
200     public void lockTokens() {
201         if (logger.isDebugEnabled()) { logger.debug("lockTokens["+ID+"] ..."); }
202 
203         for (TokenParameter token : tokens) {
204             switch (token.scope) {
205                 case READ:
206                     continue;
207                 case INPUT:
208                     token.token.lock(transition);
209                     break;
210                 case WRITE:
211                     continue;
212                 case OUTPUT:
213                     if (token.token != null) token.token.lock(transition);
214                     break;
215             }
216         }
217     }
218 
219     /**
220      * Unlocks the input and - if available - the output tokens.
221      */
222     public void unlockTokens() {
223         if (logger.isDebugEnabled()) { logger.debug("unlockTokens["+ID+"] ..."); }
224 
225         for (TokenParameter token : tokens) {
226             switch (token.scope) {
227                 case READ:
228                     continue;
229                 case INPUT:
230                     token.token.unlock();
231                     break;
232                 case WRITE:
233                     continue;
234                 case OUTPUT:
235                     if (token.token != null) token.token.unlock();
236                     break;
237             }
238         }
239     }
240 
241     /**
242      * Check conditions of this transition occurrence.
243      * Variables (e.g. <code>$input/</code>) that are expanded by the corresponding
244      * context XPath (e.g. <code>/gwdl:data/input</code>).
245      * @param to Transition occurrence to check.
246      * @return <code>ConditionChecker.CONDITION_TRUE</code> if all conditions evaluate to true and
247      * <code>ConditionChecker.CONDITION_FALSE_STATIC</code>
248      * if a least one static condition evaluates to false.
249      * <code>ConditionChecker.CONDITION_FALSE_DYNAMIC</code> if condition is false
250      * just because of time-depend conditions that may change in future.
251      * @throws WorkflowFormatException If the syntax of the condition is wrong.
252      */
253     public int checkConditions(ConditionChecker checker) throws WorkflowFormatException {
254         if (transition.getConditions().length<1) return ConditionChecker.CONDITION_TRUE;
255         if (logger.isDebugEnabled()) { logger.debug("checkConditions["+ID+"] ..."); }
256         int transCondition = checker.check(this);
257         if (logger.isDebugEnabled()) { logger.debug("checkConditions["+ID+"] ... : "+transCondition); }
258         return transCondition;
259     }
260 
261     /**
262      * Remove input tokens.
263      */
264     public void removeInputTokens() {
265         for (TokenParameter token : tokens) {
266             switch (token.scope) {
267                 case READ:
268                     continue;
269                 case INPUT:
270                     if (logger.isDebugEnabled()) {
271                         logger.debug("removeInputTokens["+ID+"]: removing token 'i:"+token.token.getID() +"' from place '"+token.edge.getPlaceID()+"'");
272                     }
273                     token.edge.getPlace().removeToken(token.token);
274                     break;
275                 case WRITE:
276                 case OUTPUT:
277                     return;
278             }
279         }
280     }
281 
282     /**
283      * Put and lock reservation dummy tokens to output places.
284      * ///ToDo: use this method to avoid place capacity problems on output places.
285      * ///ToDo: Introduce special property to be able to display them as "ghost" tokens.
286      * @throws CapacityException
287      */
288     public void putOutputReservationTokens() throws CapacityException {
289         for (TokenParameter tp : tokens) {
290             switch(tp.scope) {
291                 case OUTPUT:
292                     if (logger.isDebugEnabled()) {
293                         logger.debug("putOutputReservationTokens["+ID+"]: putting reservation token to place '"+tp.edge.getPlaceID()+"'");
294                     }
295                     Token dummyToken = Factory.newToken();
296                     dummyToken.lock(transition);
297                     tp.edge.getPlace().addToken(dummyToken);
298                     break;
299             }
300         }
301     }
302 
303     /**
304      * Remove reservation tokens from output places if there are any.
305      */
306     public void removeOutputReservationTokens() {
307         for (TokenParameter token : tokens) {
308             switch (token.scope) {
309                 case READ:
310                 case INPUT:
311                 case WRITE:
312                     continue;
313                 case OUTPUT:
314                     if (token.token != null && token.token.isLockedBy(transition)) {
315                         if (logger.isDebugEnabled()) {
316                             logger.debug("removeOutputReservationTokens["+ID+"]: removing reservation token from place '"+token.edge.getPlaceID()+"'");
317                         }
318                         token.edge.getPlace().removeToken(token.token);
319                     }
320                     break;
321             }
322         }
323     }
324 
325     /**
326      * Update tokens on write places if necessary.
327      * Currently only control tokens are updated.
328      * ///ToDo: support data write tokens. Fix sematic issues first. 
329      */
330     public void updateWriteTokens() throws CapacityException {
331         for (TokenParameter tp : tokens) {
332             switch(tp.scope) {
333                 case WRITE:
334                     if (tp.edge.getExpression() == null) { // update control token
335                         boolean newControl = activity == null || activity.getStatus() == Activity.Status.COMPLETED;
336                         // only one thread should update write tokens
337                         synchronized (tp.edge.getPlace()) {
338                             if (tp.token.getControl() == null || tp.token.getControl() != newControl) {
339                                 // update token
340                                 Token oldToken = tp.token;
341                                 tp.edge.getPlace().removeToken(oldToken);
342                                 tp.token = Factory.newToken(newControl);
343                                 if (logger.isDebugEnabled()) {
344                                     logger.debug("updateWriteTokens()["+ID+"]: replacing token 'w:"+oldToken.getID() +"' on place '"+tp.edge.getPlaceID()+"' with token 'w"+tp.token.getID()+"'");
345                                 }
346                                 addPropertiesToToken(tp.token);
347                                 tp.edge.getPlace().addToken(tp.token);
348                             }
349                         }
350                     }
351                     break;
352             }
353         }
354     }
355 
356     /**
357      * Puts output parameters to output places.
358      * This method also copies the common properties to the token.
359      * @throws CapacityException
360      */
361     public void putOutputTokens() throws CapacityException, WorkflowFormatException {
362         for (TokenParameter tp : tokens) {
363             switch(tp.scope) {
364                 case OUTPUT:
365                     if (tp.token == null) {
366                         if (tp.edge.getExpression() == null) { // generate new control token
367                             if (activity == null) {
368                                 // true control token
369                                 tp.token = Factory.newToken(true, props);
370                             } else {
371                                 // control token with Boolean representing status of activity
372                                 tp.token = Factory.newToken(activity.getStatus() == Activity.Status.COMPLETED, props);
373                             }
374                         } else {
375                             /// token contains SOAP Fault if there is no token available.
376                             String edgeExpression = tp.edge.getExpression();
377                             logger.error("Error: Output token with edge expression '" + edgeExpression + "' for "+toString()+ " is not available! Creating token with SOAP fault instead.");
378                             SOAPFault fault = new SOAPFault("env:Receiver", "No Output Available");
379                             if (activity != null) {
380                                 fault.setNode(activity.toString());
381                                 fault.setDetail("The activity '" +
382                                         activity +
383                                         "' has no output parameter related to the edge expression '" +
384                                         edgeExpression + "'. Please check the edge expression!");
385                             } else {
386                                 fault.setNode("Transition "+transition.getID());
387                                 fault.setDetail("The transition '" +
388                                         transition.getID() +
389                                         "' has no output parameter related to the edge expression '" +
390                                         edgeExpression + "'. Please check the edge expression!");
391                             }
392                             tp.token = fault.toToken();
393                         }
394                     }
395                     // copy properties
396                     addPropertiesToToken(tp.token);
397                     // add token to place
398                     if (logger.isDebugEnabled()) {
399                         logger.debug("putOutputTokens["+ID+"]: putting token 'o:"+tp.token.getID() +"' to place '"+tp.edge.getPlaceID()+"'");
400                     }
401                     tp.edge.getPlace().addToken(tp.token);
402                     break;
403             }
404         }
405     }
406 
407     public void writeWriteTokens() throws CapacityException {
408         logger.warn("WARNING: writeWriteTokens() not yet implemented!");
409         /// ToDo: Implement!
410     }
411 
412     public String getID() {
413         return ID;
414     }
415 
416     public String getIDwithOutputs() {
417         return IDwithOutputs;
418     }
419 
420     public Document getContext() {
421         return context;
422     }
423 
424     public SimpleNamespaceContext getNamespaceContext() {
425         return nsContext;
426     }
427 
428     public String getNsUri(String nsPrefix) {
429         return nsContext.translateNamespacePrefixToUri(nsPrefix);
430     }
431 
432     public ArrayList<TokenParameter> getTokens() {
433         return tokens;
434     }
435 
436     /**
437      * ToDo: execute only if necessary!
438      * As side effect, this methods adds namespaces declared by transition properties
439      * to nsContext.
440      * @param step
441      */
442     public void evaluateXPathEdgeExpressions() throws WorkflowFormatException {
443         if (logger.isDebugEnabled()) {
444             logger.debug("evaluateXPathEdgeExpressions["+ID+"] ...");
445         }
446         TokenProcessor tokenProcessor = new TokenProcessor(nsContext, context, tokens);
447         tokens = tokenProcessor.generateOutputTokens(props);
448     }
449 
450     private void updateNamespaceContextFromTransitionProperties() {
451         GenericProperties transProps = transition.getProperties();
452         for (Property prop : transProps.getProperties()) {
453             String key = prop.getKey();
454             if (key.startsWith(Constants.PROP_TRANSITION_XMLNS_)) {
455                 String[] strArr = key.split(Constants.PROP_TRANSITION_XMLNS_);
456                 if (strArr.length == 2) nsContext.addNamespace(strArr[1],prop.getValue());
457                 //                    logger.debug(" adding namespace: "+key.substring(6)+":"+prop.getValue());
458             }
459         }
460     }
461 
462     private void updatePropertiesFromToken(Token token) {
463         for (Property prop : token.getProperties().getProperties()) {
464             if (props.get(prop.getKey()) == null) {
465                 props.addProperty(prop);
466             }
467         }
468     }
469 
470     /**
471      * Put a property to the list of properties. Overwrites old values with same name.
472      * @param name
473      * @param value
474      */
475     public void putProperty(String name, String value) {
476         props.put(name,value);
477     }
478 
479 
480     /**
481      * Copy properties of transition occurrence to token.
482      * Do NOT copy property Constants.PROP_TOKEN_ACTIVITY_MAXATTEMPTS.
483      * @param token
484      */
485     private void addPropertiesToToken(Token token) {
486         for (Property prop : props.getProperties()) {
487             String key = prop.getKey();
488             if (key.equals(Constants.PROP_TOKEN_ACTIVITY_MAXATTEMPTS)) continue;
489             if (token.getProperties().get(key) == null) {
490                 token.getProperties().addProperty(prop);
491             }
492         }
493     }
494 
495     /**
496      * Remove namespaces if this element does not have any namespace prefix.
497      * This method also processes all descendands of the element.
498      * @param element
499      */
500     public static void removeNamespacesWithoutPrefix(Element element) {
501         if (element == null) return;
502         if (element.getNamespacePrefix().length()==0) element.setNamespace(null);
503         List list = element.getChildren();
504         if (list != null) {
505             for (Object child : list) {
506                 removeNamespacesWithoutPrefix((Element) child);
507             }
508         }
509     }
510 
511     /**
512      * Add the cloned content of a token to the context element, regarding the edge expression.
513      * The edge expression defines the element name of the context child element.
514      * If the edge expression contains a path (e.g., "tns:test/ns:input"), then this method inserts additional
515      * child elements (e.g., &lt;data&gt;&lt;tns:test&gt;&lt;ns:input&gt;15&lt;/ns:input&gt;&lt;/tns:test&gt;).
516      * @param token
517      * @param edge
518      */
519     private void addToContext(Token token, Edge edge) throws WorkflowFormatException {
520         String edgeExpression = edge.getExpression();
521         if (edgeExpression == null) return;
522 
523         // strip probability (e.g., "x{50%}" --> "x")
524         double probability = StringUtils.getProbability(edgeExpression);
525         if (probability > -1) {
526             if (probabilityDistribution == null) probabilityDistribution = new DoubleDistribution();
527             probabilityDistribution.put(probability);
528         }
529         String edgeExpressionWoProbabilities = StringUtils.stripProbability(edgeExpression);
530         if (edgeExpressionWoProbabilities.length() == 0) return;
531 
532         // parse edgeExpression
533         String[] edgeExpressionElements = StringUtils.stripOwl(edgeExpressionWoProbabilities).split("/");
534 
535         // control token with edge expression
536         if (token.getData() == null) {
537             Element dataChild = createTokenElement(edgeExpressionElements[0]);
538             dataChild.setText(token.getControl()?Constants.TRUE:Constants.FALSE);
539             addToContext(edgeExpression,edgeExpressionElements, dataChild);
540         }
541 
542         // data token
543         else {
544             List children = ((Element) token.getData().get()).cloneContent();
545 
546             // Replace all child elements names by edge expression, e.g., edge expression = value1 -->
547             // <data><value1>15</value1></data>
548             if (children != null) {
549                 for (Object obj : children) {
550                     if (obj instanceof Element) {
551                         // <value>15</value>
552                         Element dataChild = (Element) obj;
553                         dataChild.detach();
554                         addToContext(edgeExpression, edgeExpressionElements, dataChild);
555                     }
556                 }
557             }
558         }
559     }
560 
561     private void addToContext(String edgeExpression, String[] edgeExpressionElements, Element dataChild) throws WorkflowFormatException {
562         // only one level element hierarchy specified in edgeExpression
563         if (edgeExpressionElements.length == 1) {
564             /// we should not remove any namespaces.
565             //removeNamespacesWithoutPrefix(dataChild);
566             renameTokenElement(dataChild, edgeExpressionElements[0]);
567             context.getRootElement().addContent(dataChild);
568         }
569 
570         // more than one level in element hierarchy
571         else {
572             // traverse element hierarchy
573             Element newDataChild = null;
574             Element parent = null;
575             try {
576                 for (int i=0; i<edgeExpressionElements.length-1; i++) {
577 //                    logger.info("Processing edgeExpression part \""+edgeExpressionElements[i]+"\"");
578                     Element newelement = createTokenElement(edgeExpressionElements[i]);
579                     if (i==0) newDataChild = newelement;
580                     // connect to parent element
581                     if (parent != null) parent.addContent(newelement);
582                     parent = newelement;
583                 }
584             } catch(IllegalNameException e) {
585                 throw new WorkflowFormatException("Transition '"+transition.getID()+"' contains edgeExpression '"+edgeExpression+"' that is invalid for renaming input token elements.");
586             }
587             // last element is copied from dataChild
588             String lastEdgeExpressionElement = edgeExpressionElements[edgeExpressionElements.length-1];
589 //            logger.info("Processing edgeExpression part \""+lastEdgeExpressionElement+"\"");
590             Element lastElement = (Element) dataChild.clone();
591             renameTokenElement(lastElement, lastEdgeExpressionElement);
592             if (parent != null) parent.addContent(lastElement);
593             context.getRootElement().addContent(newDataChild);
594         }
595     }
596 
597     private void updateDataGroupProperty(String dataGroup, ArrayList<String> dataGroups) {
598         // update data group if combine.data.groups is enabled
599         if (dataGroups != null && dataGroups.size() > 0) {
600             StringBuffer str = new StringBuffer();
601             String group = dataGroups.get(0);
602             if (group != null && group.length() > 0) {
603                 str.append(group);
604             }
605             for (int i = 1 ; i < dataGroups.size() ; i++) {
606                 group = dataGroups.get(i);
607                 if (group != null && group.length() > 0) {
608                     str.append(" x ");
609                     str.append(group);
610                 }
611             }
612             props.remove(Constants.PROP_TOKEN_DATA_GROUP);
613             props.put(Constants.PROP_TOKEN_DATA_GROUP, str.toString());
614         } else if (dataGroup != null && dataGroup.length() > 0){
615             props.remove(Constants.PROP_TOKEN_DATA_GROUP);
616             props.put(Constants.PROP_TOKEN_DATA_GROUP, dataGroup);
617         }
618     }
619 
620     private void updateResourceAllocationGroupProperty() {
621         String str = transition.getProperties().get(Constants.PROP_TRANSITION_REMOVE_RESOURCE_ALLOCATION_GROUP);
622         if (str != null && !str.equals(Constants.FALSE)) {
623             for (String key : props.keys()) {
624                 if (key.startsWith(Constants.PROP_TOKEN_RESOURCE_ALLOCATION_GROUP_)) {
625                     props.remove(key);
626                 }
627             }
628         }
629     }
630 
631     /**
632      * Generate the resource.allocation.group token property from activity resource.
633      */
634     public void generateResourceAllocationGroupPropertyFromActivity() {
635         if (activity == null) return;
636 
637         String groupID = transition.getProperties().get(Constants.PROP_TRANSITION_RESOURCE_ALLOCATION_GROUP);
638         if (groupID !=null && activity.getResourceName() != null && activity.getResourceName().length() >0 ) {
639             String resourceUri = activity.getResourceName();
640             if (logger.isDebugEnabled()) {
641                 logger.debug("Resource co-allocation: setting property \""+Constants.PROP_TOKEN_RESOURCE_ALLOCATION_GROUP_ + groupID + "\" with value \""+resourceUri+"\" for activity "+activity.getID());
642             }
643             props.put(Constants.PROP_TOKEN_RESOURCE_ALLOCATION_GROUP_ + groupID, resourceUri);
644         }
645     }
646 
647     private void renameTokenElement(Element element, String newElementName) throws WorkflowFormatException {
648         String[] ne = newElementName.split(":");
649         // set new element name and namespace
650         if (ne.length > 1) {
651             // with namespace
652             String nsPrefix = ne[0];
653             // try default namespaces
654             String nsUri = getNsUri(nsPrefix);
655             if (nsUri == null) throw new WorkflowFormatException("Transition '"+transition.getID()+"' contains edgeExpression '"+newElementName+"' with namespace prefix '"+nsPrefix+"' which is not defined in the context of the transition. Use <property name=\"xmlns:"+nsPrefix+"\"/> to declare the namespace URI.");
656             element.setName(ne[1]);
657             element.setNamespace(Namespace.getNamespace(nsPrefix, nsUri));
658         } else {
659             // without namespace
660             if (!newElementName.equals("*")) {
661                 element.setName(newElementName);
662                 element.setNamespace(null);
663             }
664         }
665     }
666 
667     private Element createTokenElement(String newElementName) throws WorkflowFormatException, IllegalNameException {
668         String[] ne = newElementName.split(":");
669         if (ne.length > 1) {
670             // with namespace
671             String nsPrefix = ne[0];
672             // try default namespaces
673             String nsUri = getNsUri(nsPrefix);
674             if (nsUri == null) throw new WorkflowFormatException("Transition '"+transition.getID()+"' contains edgeExpression '"+newElementName+"' with namespace prefix '"+nsPrefix+"' which is not defined in the context of the transition. Use <property name=\"xmlns:"+nsPrefix+"\"/> to declare the namespace URI.");
675             return new Element(ne[1], Namespace.getNamespace(nsPrefix, nsUri));
676         } else {
677             // without namespace
678             return new Element(ne[0]);
679         }
680     }
681 
682     /**
683      * Copy the property from the TransitionOccurrence to all input tokens and remove it from the
684      * TransitionOccurrence.
685      * @param propertyName The name of the property.
686      */
687     public void movePropertyToInputTokens(String propertyName) {
688         String value = props.get(propertyName);
689         if (value != null) {
690             loop: for (TokenParameter token : tokens) {
691                 switch (token.scope) {
692                     case READ:
693                         continue loop;
694                     case INPUT:
695                         token.token.getProperties().put(propertyName,value);
696                         break;
697                     case WRITE:
698                     case OUTPUT:
699                         break loop;
700                 }
701             }
702         }
703     }
704 
705     public GenericProperties getProps() {
706         return props;
707     }
708 
709     public Activity getActivity() {
710         return activity;
711     }
712 
713     public void setActivity(Activity activity) {
714         this.activity = activity;
715     }
716 
717     public DoubleDistribution getProbabilityDistribution() {
718         return probabilityDistribution;
719     }
720 
721     /**
722      * Updates and returns a ID that contains the ids of all available tokens.
723      * Invoke after putOutputTokens() if you need the correct IDs of output tokens.
724      * @return
725      */
726     public String updateID() {
727         StringBuffer buffer = new StringBuffer(ID);
728         for (TokenParameter token : tokens) {
729             switch (token.scope) {
730                 case OUTPUT:
731                     if (token.token != null) {
732                         buffer.append(",o:").append(token.token.getID());
733                     }
734                     break;
735             }
736         }
737         IDwithOutputs = buffer.toString();
738         return IDwithOutputs;
739     }
740 
741     public String toString() {
742         StringBuffer buffer = new StringBuffer("TransitionOccurrence[");
743         buffer.append(transition.getID());
744         for (TokenParameter token : tokens) {
745             switch (token.scope) {
746                 case READ:
747                     buffer.append(", r:");
748                     break;
749                 case INPUT:
750                     buffer.append(", i:");
751                     break;
752                 case WRITE:
753                     buffer.append(", w:");
754                     break;
755                 case OUTPUT:
756                     buffer.append(", o:");
757                     break;
758             }
759             if (token.token != null) buffer.append(token.token.getID());
760         }
761         buffer.append(']');
762         return buffer.toString();
763     }
764 
765     public static class DefaultNamespaceContext extends SimpleNamespaceContext {
766         public DefaultNamespaceContext() {
767             super();
768             addNamespace("tns","http://target.namespace");
769             addNamespace("gwdl", "http://www.gridworkflow.org/gworkflowdl");
770             addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
771             addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
772             addNamespace("soapenv", "http://www.w3.org/2003/05/soap-envelope");
773 //            addNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
774         }
775     }
776 
777 }